Shadow DOM(影子 DOM)
元素的功能私有,不用担心与文档的其他部分发生冲突,影子 DOM(Shadow DOM)允许你将一个 DOM 树附加到一个元素上,并且使该树的内部对于在页面中运行的 JavaScript 和 CSS 是隐藏的
影子 DOM 术语
影子宿主(Shadow host): 影子 DOM 附加到的常规 DOM 节点。
影子树(Shadow tree): 影子 DOM 内部的 DOM 树。
影子边界(Shadow boundary): 影子 DOM 终止,常规 DOM 开始的地方。
影子根(Shadow root): 影子树的根节点`。`
attachShadow方法
影子DOM需要挂载到影子宿主(shadowHost),所有内置标签都会继承Element类,Element中的attachShadow方法,该方法会给指定元素挂载一个 Shadow DOM,并且返回影子树的根节点ShadowRoot的引用,
attachShadow({mode,delegatesFocus})
mode:模式,值集【open,closed】
open:外部可以访问影子根节点,通过element.shadowRoot可访问影子根节点对象 (element指得是影子宿主)
closed:外部访问不到影子根节点,element.shadowRoot返回值是null
delegatesFocus:焦点委托
创建影子DOM
//获取影子宿主
const host = document.getElementById('host')
//获取影子根节点
const shadowRoot = host.attachShadow({mode:'open'})
//添加内容
const p = document.createElement("p")
p.innerText = "我是影子DOM"
shadowRoot.appendChild(p)
影子DOM的访问
直接访问影子DOM是访问不到的,需要通过影子根节点访问,而且是在mode为open的情况下,如果为closed,则shadowRoot是null
<div class="wrapper">
<div class="title">我不是影子DOM</div>
<p>看我变了吗?</p>
</div>
<div id="host"></div>
添加影子DOM 并设置样式
const host = document.getElementById("host");
const shadowRoot = host.attachShadow({ mode: 'open' });
const div = document.createElement("div");
div.setAttribute('class',"wrapper")
div.innerHTML = "<div class='title'>我是影子DOM</div>";
const p = document.createElement("p")
p.innerText = "我咋没变呢"
div.appendChild(p)
const wrapper = Array.from(document.querySelectorAll(".wrapper"))
for (const item of wrapper) {
item.style = "width:200px;padding:10px;border:1px solid red;"
}
const ps = Array.from(document.querySelectorAll("p"))
for (const item of ps) {
item.style = "color:green"
}
shadowRoot.appendChild(div);
运行结果
通过js获取标签,修改样式,以平常我们的操作方式不起作用,我现在修改一下标签的获取逻辑,再看看效果
//注意这部分代码需要写在appendChild()方法之后,不然获取不到标签
const sPs = Array.from(host.shadowRoot.querySelectorAll("p"));
for (const item of sPs) {
item.style = "color:blue"
}
运行结果
样式变了,说明通过根节点获取标签成功了
在页面直接添加样式
.wrapper{
width:200px;
padding:10px;
border:1px solid red;
}
.title{
font-size: 20px;
}
p{
color:green
}
运行结果
从页面展示效果上看,同样的类名与标签,样式只作用于外部DOM,对影子DOM没有起作用。
影子DOM的添加样式
- 第一种,字符串拼接,添加样式标签,
const host = document.getElementById("host");
const shadowRoot = host.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
p {
color:blue
}
</style>
`
const div = document.createElement("div");
div.setAttribute('class', "wrapper")
div.innerHTML = "<div class='title'>我是影子DOM</div>";
const p = document.createElement("p")
p.innerText = "我是影子DOM中P标签"
div.appendChild(p)
shadowRoot.appendChild(div);
效果如下:
- 第二种方式 template中添加style样式
- 第三种方式
//创建一个CSSStyleSheet实例
const sheet = new CSSStyleSheet();
//使用replace()或者replaceSync()方法添加样式
sheet.replaceSync("p { color: red; font-size:20px;}");
//用过根节点属性adoptedStyleSheets,添加样式文件
shadowRoot.adoptedStyleSheets = [sheet];
效果
影子 DOM 树中定义的样式不会应用到页面的其它部分