考虑到安全及避免影子 DOM 冲突,并非所有元素都可以包含影子 DOM。尝试给无效元素或者已 经有了影子 DOM 的元素添加影子 DOM 会导致抛出错误。
以下是可以容纳影子 DOM 的元素。

任何以有效名称创建的自定义元素(参见 HTML 规范中相关的定义)<article><aside><blockquote><body><div><footer><h1><h2><h3><h4><h5><h6><header><main><nav><p><section><span>

影子 DOM 是通过 attachShadow()方法创建并添加给有效 HTML 元素的。容纳影子 DOM 的元素
被称为影子宿主(shadow host)。影子 DOM 的根节点被称为影子根(shadow root)。 attachShadow()方法需要一个 shadowRootInit 对象,返回影子 DOM 的实例。shadowRootInit 对象必须包含一个 mode 属性,值为"open"或"closed"。对"open"影子 DOM 的引用可以通过 shadowRoot属性在 HTML 元素上获得,而对"closed"影子 DOM 的引用无法这样获取。
下面的代码演示了不同 mode 的区别:

document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div> 18
`;
    const foo = document.querySelector('#foo');
    const bar = document.querySelector('#bar');
const openShadowDOM = foo.attachShadow({ mode: 'open' }); const closedShadowDOM = bar.attachShadow({ mode: 'closed' });
20 console.log(bar.shadowRoot); // null 21

一般来说,需要创建保密(closed)影子 DOM 的场景很少。虽然这可以限制通过影子宿主访问影 子 DOM,但恶意代码有很多方法绕过这个限制,恢复对影子 DOM 的访问。简言之,不能为了安全而 创建保密影子 DOM。

使用影子 DOM

把影子 DOM 添加到元素之后,可以像使用常规 DOM 一样使用影子 DOM。来看下面的例子,这里 重新创建了前面红/绿/蓝子树的示例:

   for (let color of ['red', 'green', 'blue']) {
      const div = document.createElement('div');
      const shadowDOM = div.attachShadow({ mode: 'open' });
      document.body.appendChild(div);
      shadowDOM.innerHTML = `
        <p>Make me ${color}</p>
<style> p{
          color: ${color};
        }
</style> `;
}
 console.log(openShadowDOM); // #shadow-root (open) console.log(closedShadowDOM); // #shadow-root (closed)
 console.log(foo.shadowRoot);  // #shadow-root (open)

注意 如果想保护独立的DOM树不受未信任代码影响,影子DOM并不适合这个需求。 对iframe施加的跨源限制更可靠。

虽然这里使用相同的选择符应用了 3 种不同的颜色,但每个选择符只会把样式应用到它们所在的影 子 DOM 上。为此,3 个

元素会出现 3 种不同的颜色。
可以这样验证这些元素分别位于它们自己的影子 DOM 中:

   for (let color of ['red', 'green', 'blue']) {
      const div = document.createElement('div');
      const shadowDOM = div.attachShadow({ mode: 'open' });
      document.body.appendChild(div);
      shadowDOM.innerHTML = `
        <p>Make me ${color}</p>
<style> p{
          color: ${color};
        }
</style> `;
}
    function countP(node) {
      console.log(node.querySelectorAll('p').length);
}
    countP(document); // 0
    for (let element of document.querySelectorAll('div')) {
      countP(element.shadowRoot);
}
// 1 // 1 // 1

更多推荐