YIXUNFE / blog

文章区
151 stars 25 forks source link

认识 Shadow DOM #10

Open YIXUNFE opened 8 years ago

YIXUNFE commented 8 years ago

认识Shadow DOM

你知道 Shadow DOM 吗?这个概念你可能不清楚,但我可以确定,你肯定遇见过它们。


Shadow DOM 在哪里

我们来看一个在 Chrome 开发者工具下的截图。

shadow dom

有没有看见一个 #shadow-root 标记?那就是 Shadow DOM 啦。

上图中的 Shadow DOM 是笔者通过 JS 创建的,我们来看一个原生的例子。

video element

一个简单的 <video></video> 元素中原来包含了一个大大的 DOM 树,都在 shadow-root 下,而且在其中还包含了一个子 shadow-root,真是神奇。video 元素在界面上呈现的UI控制看来都是用 Shadow DOM 方式实现的。

查看方法

怎么查看?你可以打开 Chrome 开发者工具,在设置中勾选上显示 Show user agent shadow DOM 选项,就可以查看页面中是否有 Shadow DOM 了。

chrome dev tool


Shadow DOM 的创建

一般的 DOM 对象支持一个叫做 createShadowRoot 的方法,这个方法可以给该 DOM 对象创建了一个 shadow root 对象,然后你就可以在这个 root 对象里放你想要的内容了。

var root = document.body.createShadowRoot()
root.innerHTML = '...'

上面的代码中,我们在 body 元素下创建了一个 shadow root 对象。

我们来看看一些相关术语:

shadow host (宿主)

指一个承载了一个或多个节点树的元素(至少有一个是 shadow tree)。

shadow tree

指一个在 shadow host 中的节点树。

shadow root

指在 shadow tree 中的根元素。

shadow tree

从上图中我们可以看出三者的关系。


Shadow DOM 特性

知道了网页中有 Shadow DOM 的存在,那么它有什么样的特性呢?

游离在外

我们知道 DOM片段 (document fragment),是一棵游离在主 DOM 树之外的节点树,它的创建基于 document 对象。当我们将它作为一个子元素插入到主 DOM 树后,它将变成主 DOM 树的一部分。

Shadow 树也是游离在 DOM 树之外的节点树,但是他的创建基于普通 DOM 元素(非 document),并且创建后的 Shadow 树节点可以从界面上直观的看到。更重要的是,Shadow DOM 具有良好的密封性。

密封性

在 Shadow 树中,我们只有通过伪元素等进行对内部元素的样式访问,这样可以有效的避免样式冲突。

CSS 渲染 Shadow DOM 的方法:

/*匹配元素下的 shadow  tree 中的节点*/
selector::shadow  selector {...}

/*匹配祖先元素下的 shadow  tree 中的节点*/
selector /deep/ selector {...}

/*匹配 shadow  tree 中的 content元素内引用的节点*/
::content > selector {...}

另外还有找到宿主元素的方法,就是使用 :host:host-content 伪类。

/*匹配宿主元素*/
:host {...}

/*匹配符合 selector 的宿主元素*/
:host(selector) {...}

/*匹配在 shadow  tree 中拥有符合 selector 的元素的宿主元素*/
:host-content(selector) {...}

content 标签

如果你在浏览器下尝试过为一个元素创建一个 Shadow Root,那么你可能会发现,在该元素下创建 Shadow Root 后,该元素中原有的子元素都看不见了。这是由于浏览器不会渲染该元素中的内容,而仅渲染创建的 Shadow Root 中的内容,那么当我们还需要显示一部分原来节点中的内容,该怎么办呢?

虽说界面上看不见了,但是该元素下的子元素都还在,我们可以在 Shadow Root 中使用 content 标签来解决上面的问题。

<div>
    <span class="ele">我是子元素</span>
    <span class="ele">我是子元素</span>
    #shadow-root
        <content select=".ele"></content>
</div>

content 标签的 select 属性可以赋值一个选择器,代表你需要放入的元素。上面的例子中我们将两个 span 元素放在了 content 中。


使用 Shadow DOM 的意义

由于以前的组件开放方式是开放式的,即最终生成的 HTML 结构难以与组件外部结构区分,样式容易互相混淆,Shadow DOM 为我们提供了解决这些问题的可能。在 Web 组件化的规范中也可以看到 Shadow DOM 的身影,使用具有良好密封性的 Shadow DOM 开发下一代 Web 组件将会是一种趋势。


浏览器兼容性

caniuse

Firefox下需要手动开启 Shadow DOM 设置


THANKS