Shadow DOM Styling

Shadow DOM Styling (阴影DOM样式)

Shadow DOM encompasses both <link rel=“stylesheet” href="…"> and <style>. For the first case, the style sheets are HTTP-cached. Hence, they can’t be re-downloaded for multiple components that apply similar templates.

The primary rule is that local styles can operate only within the shadow tree. But, the document styles work out of that tree. Of course, there can be exceptions to the rule. (主要规则是本地样式只能在阴影树中操作。但是,文档样式可以从该树中发挥作用。当然,这条规则也有例外。)

:host

:host

Let’s explore the :host selector. With it, you can select the shadow host. For instance, let’s try to create <custom-dialog> element that needs to be centered. To meet that goal, we should style the <custom-dialog> element.

Here is how the :host selector operates:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
   <head>
 <body>
   <template id="tmpId">
     <style>
       /* the style will be applied internally to the custom-dialog element */
(/*样式将在内部应用于自定义对话框元素*/)
       :host {
       position: fixed;
       transform: translate(-50%, -50%);
       left: 50%;
       top: 50%;
       display: inline-block;
       border: 2px solid blue;
       padding: 15px;
       }
     </style>
     <slot></slot>
   </template>
   <custom-dialog>  Welcome to w3cdoc </custom-dialog>
   <script>
     customElements.define('custom-dialog', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'}).append(tmpId.content.cloneNode(true));
       }
     });
   </script>
 </body>
</html>

Cascading

Cascading (级联)

The shadow host (<custom-dialog>) exists in the light DOM. Hence, it obeys the document CSS rules.

In case the property is styled both in the :host and in the document, then the document will take the precedence.

So, if in the document there is:

<style>
 custom-dialog {
 padding: 0;
 }
</style>

Then, <custom-dialog> will be without any padding.

That is very handy, as it allows setting up the default component style in the :host rule. Then, you can override them within the document. There is an exception: once the property is labeled as !important, then the local style will take the precedence for such properties.

:host(selector)

:host(selector)

It is similar to the :host but is used in case the host matches the selector.

Here is an example:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
   <head>
 <body>
   <template id="tmpId">
     <style>
       :host([center]) {
       position: fixed;     
       transform: translate(-50%, -50%);
       left: 50%;
       top: 50%;
       border-color: blue;
       }
       :host {
       display: inline-block;
       border: 2px solid red;
       padding: 15px;
       }
     </style>
     <slot></slot>
   </template>
   <custom-dialog center>
     Centre! Welcome to w3cdoc.
(中心!欢迎来到w3cdoc。)
   </custom-dialog>
   <custom-dialog>
     Not centre. Welcome site.
(不是中心。欢迎站点。)
   </custom-dialog>
   <script>
     customElements.define('custom-dialog', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'}).append(tmpId.content.cloneNode(true));
       }
     });
   </script>
 </body>
</html>

As a result, the extra centering styles are used only for the first dialog <custom-dialog centered>.

:host-context(selector)

:host-context(selector)

It is also the same as :host but used only when the shadow host or its ancestors in the outer document correspond to the selector.

Here is an example:

<body class="dark-theme">
 <!-- :host-context(.dark-theme) applies to custom-dialogs inside .dark-theme -->
 <custom-dialog>...</custom-dialog>
</body>

To be more precise, the :host-family of selectors can be used for styling the main element of the component, resting on the context.

Those styles (except for the !important) might be overridden by the document. (这些样式(! important除外)可能会被文档覆盖。)

Styling Slotted Content

Styling Slotted Content (开槽内容的样式)

In this section, we are going to consider cases with the slots. (在本节中,我们将考虑具有插槽的案例。)

The slotted elements originate from the light DOM. It means that they use document styles. The slotted content is not affected by local styles. (开槽元素源自光DOM。这意味着他们使用文档样式。插槽内容不受本地样式的影响。)

Let’s see an example where the slotted <span> is bold as part of the document style, yet it doesn’t take the background from the local style.

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <style>
     span { font-weight: bold }
   </style>
   <site-card>
     <div slot="sitename"><span>w3cdoc</span></div>
   </site-card>
   <script>
     customElements.define('site-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'});
         this.shadowRoot.innerHTML = `
           <style>
           span { background: blue; }
           </style>
           Sitename: <slot name="sitename"></slot>
         `;
       }
     });
   </script>
 </body>
</html>

So, the result will be bold but not red. (因此,结果将是粗体,但不是红色。)

In case you want to style the slotted elements in the components, you can choose among two options. (如果要为组件中的开槽元素设置样式,可以从两个选项中进行选择。)

According to the first one, you can style the <slot>, relying on the CSS inheritance, like this:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <site-card>
     <div slot="sitename"><span>w3cdoc</span></div>
   </site-card>
   <script>
     customElements.define('site-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'});
         this.shadowRoot.innerHTML = `
           <style>
           slot[name="sitename"] { font-weight: bold; }
           </style>
           Sitename: <slot name="sitename"></slot>
         `;
       }
     });
   </script>
 </body>
</html>

The second option is using the ::slotted(selector) pseudo-class. It corresponds to elements based on the following two conditions:

It’s a slotting element, coming from the light DOM. No matter what the slot name is. But it relates only to the element itself, not the children. The element corresponds to the selector. (这是一个开槽元素,来自光DOM。 无论插槽名称是什么。 但它只与元素本身有关,而不与子元素有关。 元素对应于选择器。)

So, in the example, we demonstrate, the ::slotted(div) selects exactly <div slot=“username”>, not the children of it:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <site-card>
     <div slot="sitename">
       <div>w3cdoc</div>
     </div>
   </site-card>
   <script>
     customElements.define('site-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'});
         this.shadowRoot.innerHTML = `
           <style>
           ::slotted(div) { border: 2px solid blue; }
           </style>
           Sitename: <slot name="sitename"></slot>
         `;
       }
     });
   </script>
 </body>
</html>

Let’s consider another important thing: the ::slotted selector will not be descended any further in the slot. The selectors like this are invalid:

::slotted(div span) {
 /* our <div> slot does not match this */
}

::slotted(div) p {
 /* cannot enter the light of the DOM */
}

Moreover, ::slotted may be used only in CSS. It can’t be used in querySelector.

CSS Hooks with Custom Properties

CSS Hooks with Custom Properties (带自定义属性的CSS钩子)

No selector can be directly affected by shadow DOM styles from the document. But like exposing methods to interact with the component, CSS variables can be exposed for styling it. (任何选择器都不能直接受到文档中阴影DOM样式的影响。但是,就像暴露与组件交互的方法一样, CSS变量可以暴露用于样式化它。)

Custom CSS properties are both in the light DOM and in the shadow DOM. For instance, in shadow DOM, the –user-card-field-color CSS variable can be used for styling fields. The outer value can set its value like this:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <style>
     .field {
     color: var(--user-card-field-color, black);
     /* if - user-card-field-color not defined, use black */
(/*如果-未定义用户卡字段颜色,请使用黑色*/)
     }
   </style>
   <div class="field">
     Name: 
     <slot name="username"></slot>
   </div>
   <div class="field">
     Id: 
     <slot name="id"></slot>
   </div>
   </style>
 </body>
</html>

That property can be declared in the outer document for <user-card> like this:

user-card {
 --user-card-field-color: blue;
}

Custom CSS properties are visible anywhere. So, the inner .field rule can make use of that. (自定义CSS属性在任何地方都可见。因此,内部.field规则可以利用这一点。)

Let’s take a look at the full example:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <style>
     user-card {
     --user-card-field-color: blue;
     }
   </style>
   <template id="tmpId">
     <style>
       .field {
       color: var(--user-card-field-color, black);
       }
     </style>
     <div class="field">
       Name: 
       <slot name="username"></slot>
     </div>
     <div class="field">
       Id: 
       <slot name="id"></slot>
     </div>
   </template>
   <script>
     customElements.define('user-card', class extends HTMLElement {
       connectedCallback() {
         this.attachShadow({mode: 'open'});    this.shadowRoot.append(document.getElementById('tmpId').content.cloneNode(true));
       }
     });
   </script>
   <user-card>
     <span slot="username">Jack Brown</span>
     <span slot="id">112</span>
   </user-card>
 </body>
</html>

Summary

Summary (概要)

Shadow DOM includes styles such as <link rel=“stylesheet”> or <style>. The local styles can impact on:

  • shadow tree (-影子树)

  • shadow host along with the pseudo-classes of :host-family.

  • slotted elements but not their children. (-插槽元素,但不是他们的子元素。)

The document styles can impact on:

  • shadow host (-影子主机)

  • slotted elements along with their contents (-开槽元素及其内容)

Once there is a conflict between the CSS properties, as a rule, document styles take precedence. The exception is when the property is labeled as !important. In such a case, local styles take precedence. (通常,一旦CSS属性之间存在冲突,文档样式将优先。例外情况是当属性标记为! important时。在这种情况下,本地风格优先。)



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部