ข้ามไปยังเนื้อหา

การจัดสไตล์เนื้อหาใน Slot

pseudo-element ::slotted(selector) ช่วยให้คุณเขียน CSS rules ภายใน shadow root เพื่อกำหนดเป้าหมาย node ใน light DOM ที่ถูก project เข้าไปใน <slot> เมื่อผู้ใช้วาง <p> หรือ <strong> ไว้ภายใน custom element ของคุณ node เหล่านั้นยังคงอยู่ใน light DOM แต่ถูก render ให้เห็นภายใน shadow tree — ::slotted() คือวิธีจัดสไตล์ node เหล่านั้นจากภายใน

::slotted(p) { color: steelblue; }
::slotted(strong) { color: #e11d48; }

rule ข้างต้นอยู่ภายในบล็อก <style> ของ shadow ของ component element <p> หรือ <strong> ใดๆ ที่ผู้ใช้ slot เข้ามาใน component จะได้รับสไตล์เหล่านั้น

::slotted() สามารถ match ได้เฉพาะ node ที่ถูก slot โดยตรงระดับบนสุด — children โดยตรงของ host ที่ตกลงไปอยู่ใน slot ไม่สามารถเข้าถึง subtree ของ element ที่ถูก slot ได้:

  • ::slotted(div span) ไม่ ทำงาน — คุณไม่สามารถกำหนดเป้าหมาย <span> ที่ซ้อนอยู่ภายใน <div> ที่ถูก slot ได้
  • เฉพาะ compound selectors ที่มี slotted element ระดับเดียวเท่านั้นที่ถูกต้อง
  • specificity ของ ::slotted() ต่ำ — เทียบเท่ากับ (0,0,1,0) — ดังนั้นสไตล์ของหน้าบน element เดียวกันสามารถชนะได้

หากคุณต้องการควบคุม subtree ที่ถูก slot ในระดับลึกขึ้น ให้ปรับโครงสร้าง HTML ของคุณเพื่อ slot element ในสุดโดยตรง หรือส่ง CSS custom properties เข้าไปใน shadow root จากหน้า

ตัวเลือกBenefitCost
::slotted() จาก shadow rootผู้เขียน component กำหนด default style ให้เนื้อหาที่ถูก slot ได้ โดยผู้ใช้ไม่ต้องเขียน CSS เองเลยmatch ได้เฉพาะ node ที่ถูก slot โดยตรงระดับบนสุดเท่านั้น ควบคุม subtree ลึกไม่ได้ และ specificity ต่ำ ทำให้ page CSS override ได้ง่าย
ปล่อยให้หน้าจัดสไตล์ light DOM เอง / ส่ง custom properties เข้าไปผู้ใช้ควบคุม styling ของเนื้อหาตัวเองได้เต็มที่อยู่แล้วเพราะเป็น light DOM ไม่ต้องพึ่ง ::slotted() เลยcomponent author เสียการควบคุม default appearance ถ้าไม่ตั้ง baseline ไว้เลย เนื้อหาที่ slot เข้ามาอาจดูไม่สอดคล้องกับ component
  • คาดหวังว่า descendant selector ใน ::slotted() จะทำงาน — เขียน ::slotted(p) span {} แล้วคาดว่าจะจัดสไตล์ span ที่ซ้อนอยู่ใน p ที่ถูก slot ได้ แต่ไวยากรณ์นี้ ไม่ถูกต้อง::slotted() match ได้แค่ตัว element ที่ถูก slot โดยตรงระดับบนสุดเท่านั้น ไม่ใช่ descendant ของตัวเอง
  • ใช้ compound selector ลึกอย่าง ::slotted(div span) — คาดว่าจะกำหนดเป้าหมาย span ที่ซ้อนอยู่ใน div ที่ถูก slot ได้ แต่ spec อนุญาตให้ match ได้เฉพาะ compound selector ระดับเดียวกับ slotted node เท่านั้น
  • ลืมว่า specificity ของ ::slotted() ต่ำ — เทียบเท่ากับ (0,0,1,0) เท่านั้น ทำให้สไตล์ของหน้าที่กำหนดเป้าหมาย element เดียวกันโดยตรงสามารถชนะได้อย่างเงียบ ๆ โดยไม่ต้องใช้ !important

💡 ตัวอย่างจากของจริง

Shoelace — component อย่าง <sl-card> ใช้ ::slotted() ตั้ง margin เริ่มต้นให้ content ที่ผู้ใช้ slot เข้ามา เผื่อผู้ใช้ไม่ได้ตั้งสไตล์เอง โดยยอมรับข้อจำกัดว่าปรับได้แค่ node ระดับบนสุด

Adobe Spectrum (Spectrum Web Components) — เลือกส่ง CSS custom properties เข้าไปในบาง component แทนการพึ่ง ::slotted() ลึก ๆ เพราะโครงสร้างเนื้อหาที่ผู้ใช้ slot เข้ามามักซับซ้อนเกินกว่าที่ selector ระดับตื้นจะจัดการได้

::slotted(p) กำหนดเป้าหมายอะไร?
::slotted(div span) สามารถกำหนดเป้าหมาย span ที่ซ้อนอยู่ใน div ที่ถูก slot ได้หรือไม่?
rule ::slotted() เขียนไว้ที่ไหน?