如何在Shadow DOM/Web組件中覆蓋CSS
Web組件(Web Components)的主要目的之一是提供封裝——能夠隱藏HTML標(biāo)記結(jié)構(gòu)和CSS樣式,并與頁面上的其他代碼分離,這樣不同的部分就不會沖突,通過這種方式,這樣代碼就可以保持漂亮和干凈。
影子DOM(Shadow DOM)為我們提供了范圍限定的樣式封裝,并提供了一種讓我們隨意選擇進入(盡可能少)外界的方法。
但是,如果我想使我的組件可自定義某些樣式屬性,該怎么辦?
本文介紹了使用CSS自定義屬性穿透Shadow DOM并使你的Web組件可自定義的基礎(chǔ)知識。
創(chuàng)建一個HTML元素
我們將使用擴展基本HTML Element的JavaScript類創(chuàng)建自定義HTML元素。然后,我們將使用要創(chuàng)建的標(biāo)簽名稱和剛剛創(chuàng)建的類調(diào)用 customElements.define()。
- class AppCard extends HTMLElement {...}
- window.customElements.define('app-card', AppCard);
在此示例中,我們將創(chuàng)建此簡單的Material Design卡片,當(dāng)我們在HTML上添加此元素時將顯示該元素:<app-card></ app-card>

首先,我們創(chuàng)建Shadow DOM root,然后將HTML和CSS字符串分配給Shadow DOM root 的 innerHTML,如下所示。
- class AppCard extends HTMLElement {
- constructor() {
- super();
- const shadowRoot = this.attachShadow({mode: 'open'});
- shadowRoot.innerHTML = `
- <style>
- .card {
- background-color: #fff;
- ...
- }
- </style>
- <div class="card">
- <div>Card title</div>
- </div>
- `;
- }}window.customElements.define('app-card', AppCard);
覆蓋嘗試
在此示例中,我們要修改卡的背景顏色。如果它是HTML中的簡單 div 元素,則可以覆蓋 card 類或通過CSS選擇器訪問 div 元素。但是,以下嘗試將無效:
- // access the div
- app-card > div {
- background-color: #2196F3;
- }
- // override card class
- app-card > .card {
- background-color: #2196F3;
- }
使用CSS自定義屬性
為了解決這個問題,我們可以使用CSS自定義屬性(CSS變量)??梢允褂肅SS中定義的CSS自定義屬性來更改自定義元素中的某些CSS屬性。
按照我們的例子,我們將使用屬性 background-color 上的變量 card-bg 來獲取誰在使用自定義元素所定義的顏色。
- class AppCard extends HTMLElement {
- constructor() {
- super();
- const shadowRoot = this.attachShadow({mode: 'open'});
- shadowRoot.innerHTML = `
- <style>
- .card {
- background-color: var(--card-bg, #fff);
- ...
- }
- </style>
- <div class="card">
- <div>Card title</div>
- </div>
- `;
- }}window.customElements.define('app-card', AppCard);
現(xiàn)在,我們將使用 app-card 自定義元素,并在Body元素的CSS中創(chuàng)建 card-bg 變量,我們將十六進制顏色 #2196F3 分配給變量。
- <html>
- <head>
- <style>
- body {
- --card-bg: #2196F3;
- }
- </style>
- </head>
- <body>
- <app-card></app-card>
- </body>
- </html>

總結(jié)
使用這種策略,我們可以在你的文檔中擁有一個封裝的CSS元素,同時我們可以允許使用CSS對一些屬性進行自定義。你可以在這里訪問一個完整的例子。