Shadow DOM 的用途是封装隔离一个元素,从而在组件化的过程中隔离外部样式和 JS 的影响。
使用
创建
通过在一个元素(A)上创建一个 shadow root 的方式来创建 shadow dom,返回的值成为 shadow root,A 成为 shadow host。
一旦创建了 shadow root,那么宿主元素的原内容将不被展示,而是展示 shadow root 的内容。
可以在一个 shadow host 上创建多个 shadow root(但只有最新的一个会生效,但可以通过 <shadow></shadow> 来内嵌引用前一个 shadow;并且目前无法移除 shadow root),还可以把一个 shadow root 里的元素作为 shadow host 来继续创建 shadow root。
1 2 3 4 5 6 | |
通过配合使用 html template 可以简化创建时的繁琐,如:
1 2 3 4 | |
相关属性
element.shadownRoot:获取一个元素挂着的生效的 shadow rootshadowRoot.host:获取 shadow root 的宿主元素
引用、多个和嵌套
引用 host 内容
创建了 shadow root 后,宿主元素的原内容将不被展示,而是展示 shadow root 的内容,但可以在 shadow root 中使用<content></content> 来引用宿主直接子元素的内容。
下面假定 #host 表示 shadow root 的 host,shadow root 的内容是 #template 里的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
对于上面的代码,#host 标签里的原内容会被投射到 div.main 里。
content 是表示选择什么内容来投射到当前位置,默认是投射全部内容。
可以使用 select="immediate_child_selector" 来选择 host 里直接子元素来投射,如 <content select="p"></content> 可以引用 p;<content select="span"></content> 来引用 span1 和 span2,但不会引用 span0,因为 span0 不是直接子元素。
可以有多个 <content> 标签,但被引用的内容仅能被引用一次,先到先得,后来的将只能在挑剩的里面找匹配的。
1 2 3 4 5 6 7 8 9 10 11 12 | |
上面的例子中, <content select=".a, .b"></content> 引用了 div0,div1 和 div2,那么位于其后的 <content select="div"></div> 将只能匹配到剩下的一个 div3。
多个 shadow root 和嵌套
对于一个宿主,可以创建多个 shadow root,但仅有最新的一个会生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
上面最终将只展示 div.c。
嵌套
但如果在 #t3 的 <content> 后添加 <shadow></shadow>,那么则可以引用上一个 shadow root(可以看做是 FILO 的栈),这样展示的 div.c 后会展示 div.b。还可以在 #t2 的 <content> 后添加 <shadow></shadow>,那么会在 div.b 后展示 div.a。
需要注意的是,嵌套时,如果使用了 content(有或没 select),那么最外层的 shadow root 引用了的元素将不会被内层的 shadow root 再次引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
样式
shadow root 隔离了普通的的样式,这意味外面的样式影响不了里面的,而里面的也影响不了外面的。
但还是可以通过特定的选择器来在内部对 host 或从外部对内部设置样式。
在内部设置 host 的样式
1 2 3 4 5 6 7 8 9 10 11 | |
:host,对 host 设置样式:host(selector),如果 host 匹配了 selector,那么设置样式:host-context(selector),如果 host 或其祖先元素匹配了 selector,那么设置样式
在外部影响内部样式
1 2 3 4 5 6 7 8 9 10 11 | |
::shadow,选择当前 host 的 shadow root/deep/,选择其 shadow root、子元素的 shadow root 和嵌套的 shadow root
投射的元素的样式
通过 <content> 来投射的元素的样式不是在 shadow root 内生效的,而是受原节点结构相关联的样式影响。这是因为投射过去的元素并不是在 shadow root 内,那只是一个渲染节点而已。
如果需要在 shadow root 内设置投射过来的元素的样式,那么需要使用 ::content 伪元素来选择对应的 content 映射空间,然后再接上待匹配的元素的选择器即可,如 ::content[select=div] .header。
遍历
遍历 shadow root 和其后代元素,可以通过 element.shadowRoot 来获取 shadow root,然后通过选择器 api(querySelector 等) 来获取后代元素。
但需要注意的是,通过 content 来投射的内容并不能这样获取,因为其并不在 shadow 树上。这需要在 content 节点上调用 getDistributedNodes() 来获取所有被引用的节点列表(NodeList)。