头图

前言

在上一节我们了解了如何给自定义组件设置样式,当时是将自定义标签的样式设置在主 DOM 中的:

<style>
    my-card {
        display: block;
        margin: 20px;
        width: 200px;
        height: 200px;
        border: 3px solid #000;
    }
</style>
<my-card></my-card>

虽然实现了样式设置的目的,但是却存在一个弊端:自定义标签的样式被写死了,不够灵活。

如果能够在自定义组件内部控制自定义标签的样式,那样的话会相对灵活,而且也算是实现了”封装、相互隔离“的组件原则。今天,我们就来学习一下如何在自定义组件内部实现自定义标签的样式控制。

在正文开始之前,我们再复习一下 Shadow DOM 的整体结构:

image-20220209182955624

Shadow DOM 的 CSS 选择器

今天的重点是认识与 Shadow DOM 相关的几个选择器。

:host 伪类选择器

选取内部使用该部分 CSS 的 Shadow host 元素,其实也就是自定义标签元素。用法如下:

:host {
    display: block;
    margin: 20px;
    width: 200px;
    height: 200px;
    border: 3px solid #000;
}

注意::host 选择器只在 Shadow DOM 中使用才有效果。

比如:

image-20220216185103096

另外,可以使用 :host 子选择器 的形式来给 Shadow Host 的子元素设置样式,比如:

image-20220216185355256

:host 伪类选择器的兼容性如下:

image-20220216191419476

:host()伪类函数

:host() 的作用是获取给定选择器的 Shadow Host。比如下面的代码:

<my-card class="my-card"></my-card>
<my-card></my-card>

<script>
    class MyCard extends HTMLElement {
        constructor () {
            super();
            this.shadow = this.attachShadow({mode: "open"});
            let styleEle = document.createElement("style");
            styleEle.textContent = `
                :host(.my-card){
                    display: block;
                    margin: 20px;
                    width: 200px;
                    height: 200px;
                    border: 3px solid #000;
                }
                :host .card-header{
                    border: 2px solid red;
                    padding:10px;
                    background-color: yellow;
                    font-size: 16px;
                    font-weight: bold;
                }
            `;
            this.shadow.appendChild(styleEle);


            let headerEle = document.createElement("div");
            headerEle.className = "card-header";
            headerEle.innerText = "My Card";
            this.shadow.appendChild(headerEle);
        }
    }

    window.customElements.define("my-card", MyCard);

</script>

:host(.my-card) 只会选择类名为 my-card 的自定义元素, 且它后面也可以跟子选择器来选择自己跟节点下的子元素。

需要注意的是::host() 的参数是必传的,否则选择器函数失效,比如:

image-20220216192613676

:host() 伪类函数的兼容性如下:

image-20220216191512610

:host-context()伪类函数

用来选择特定祖先内部的自定义元素,祖先元素选择器通过参数传入。比如以下代码:

<div id="container">
    <my-card></my-card>
</div>
<my-card></my-card>
<script>
    class MyCard extends HTMLElement {
        constructor () {
            super();
            this.shadow = this.attachShadow({mode: "open"});
            let styleEle = document.createElement("style");
            styleEle.textContent = `
                :host-context(#container){
                    display: block;
                    margin: 20px;
                    width: 200px;
                    height: 200px;
                    border: 3px solid #000;
                }
                :host .card-header{
                    border: 2px solid red;
                    padding:10px;
                    background-color: yellow;
                    font-size: 16px;
                    font-weight: bold;
                }
            `;
            this.shadow.appendChild(styleEle);


            let headerEle = document.createElement("div");
            headerEle.className = "card-header";
            headerEle.innerText = "My Card";
            this.shadow.appendChild(headerEle);
        }
    }

    window.customElements.define("my-card", MyCard);

</script>

:host-context(#container) 只会对 id 为 container 元素下的自定义元素生效,效果如下:

image-20220216193941726

注意:这里的参数也是必传的,否则整个选择器函数不生效。

其兼容性如下:

image-20220216200336292

:host:host()共存的必要性

看完上面的介绍后,不少人可能会有这样一个疑惑::host(.my-card){} 不是可以直接用 :host.my-card{} 代替?

答案是不可以!!!,因为::host.my-card 实质上的意思是找 .my-card (Shadow root)的 :host(Shadow Host) ,这 Shadow DOM 的从结构上来说就已经互相矛盾了。

总结

以上就是关于 Shadow Host 的 CSS 选择器内容,总结一下:

  • :host 范围最大,匹配所有的自定义元素实例;
  • :host() 只选择自身包含特定选择器的自定义元素;
  • :host-context() 选择拥有特定选择器父元素的自定义元素。

~

~ 本文完,感谢阅读!

~

学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!


编程三昧
54 声望10 粉丝

学习有趣的知识,交识有趣的朋友,造就有趣的灵魂!