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

การ Encapsulate สไตล์

Shadow boundary คือเส้นกั้นที่ป้องกันไม่ให้ CSS รั่วผ่าน shadow root เส้นกั้นนี้ทำงานสองทิศทาง:

  • สไตล์ที่ประกาศ ภายใน shadow root จะไม่ส่งผลต่อ element ใดๆ ใน light DOM
  • selector CSS จาก ภายนอก shadow root ไม่สามารถกำหนดเป้าหมาย element ภายใน shadow tree ได้

ลักษณะนี้เรียกว่า style encapsulation และเป็นหนึ่งในคุณประโยชน์หลักของ Shadow DOM

ลองดู shadow element ที่ประกาศ .box { color: red; } ไว้ภายใน shadow root:

class EncDemo extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML =
'<style>.box { color: red; font-weight: bold; }</style>' +
'<div class="box">Red — shadow style</div>';
}
}

กฎ .box { color: red; } นี้ไม่มีผลใดๆ เลยต่อ element .box ที่อยู่นอก component ตัวนี้

ลองดู CSS ระดับหน้าที่กำหนดเป้าหมายไปที่ .box:

/* page stylesheet */
.box {
color: blue;
font-weight: bold;
border: 2px solid blue;
}

กฎนี้ใช้กับ .box ทุกตัวใน light DOM แต่ไม่ใช้กับ .box ที่อยู่ภายใน shadow root ของ <enc-demo> shadow boundary กันมันไว้

ตัวอย่างด้านล่างมี .box สอง element ที่ใช้ class name เดียวกัน ตัวที่อยู่นอก custom element ถูกสไตล์เป็นสีฟ้าโดย page CSS ส่วนตัวที่อยู่ใน shadow root ถูกสไตล์เป็นสีแดงโดย shadow CSS ไม่มีสไตล์ไหนข้ามขอบเขตไปหาอีกฝั่งเลย

ตัวเลือกBenefitCost
Style encapsulation (shadow boundary)สไตล์ของ component ไม่ชนกับสไตล์หน้าโดยไม่ตั้งใจ ปลอดภัยต่อการนำไปใช้ซ้ำธีมจากภายนอกเข้าถึง internal ไม่ได้โดยตรง ต้องพึ่ง CSS custom properties หรือ ::part แทน
ไม่ใช้ Shadow DOM (global CSS ธรรมดา)ปรับสไตล์จากภายนอกได้อิสระ ไม่ต้องเปิด API เพิ่มเสี่ยง class name ชนกัน และสไตล์รั่วเข้า-ออกโดยไม่ตั้งใจเมื่อโปรเจกต์โต
  • คาดหวังให้ CSS หน้าเว็บ selector ธรรมดาเข้าไปสไตล์ภายใน shadow root ได้ — เช่นเขียน .my-widget .title { color: red } แล้วสงสัยว่าทำไมไม่ทำงาน shadow boundary ถูกออกแบบมาให้กันแบบนี้โดยเจตนา
  • ลืมว่า class name ข้างในและข้างนอก shadow root เป็นคนละ scope — ตั้ง .box ทั้งสองฝั่งแล้วคิดว่าต้องแก้ไขให้ไม่ชนกัน ทั้งที่จริงๆ ไม่ชนกันอยู่แล้วเพราะ encapsulation
  • ไม่รู้ว่ามี escape hatch อย่าง CSS custom properties และ ::part — พอ styling จากภายนอกไม่ได้เลยเลิกใช้ Shadow DOM ทั้งที่ควรออกแบบ custom property API ให้ theme ได้แบบควบคุมได้

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

Salesforce Lightning (LWC) — ใช้ style encapsulation เพื่อให้ component นับพันตัวจากทีมต่างๆ ไม่ชนสไตล์กันใน enterprise app ขนาดใหญ่ (salesforce/lwc)

CSS ของหน้าประกาศ `.title { color: red; }` กฎนี้จะส่งผลต่อ element `.title` ภายใน shadow root หรือไม่?
shadow root มี `<style>p { font-size: 2rem; }</style>` กฎนี้ส่งผลต่อ element ใด?
อะไรที่สามารถข้าม shadow boundary ได้?
ประโยชน์หลักของ style encapsulation ใน Shadow DOM คืออะไร?