相比<p>、<div>这样的标签,
input标签表现得更加复杂,而它是怎么实现的呢?
在这里我们要引出一个概念
Shadow DOM
Shadow DOM是HTML的一个规范 ,它允许浏览器开发者封装自己的HTML标签、CSS样式和特定的javascript代码,同时也可以让开发人员创建类似<video>这样的自定义一级标签,创建这些新标签内容和相关的的API被称为Web Component。
Web Component内容比较多不在这里讲,有空再写一篇
在平时我们是没办法从DevTools中看到Shadow DOM的,因为默认是隐藏的,需要手动设置,
来一个例子,我们来看看一个input组件
在没有显示Shadow DOM之前 它在HTML中是长这个样子的
展开之后 是这个样子
可以看出在input标签内出现了#shadow-root(user-agent),它包含了input组件内的DOM结构,
这就是所谓的Shadow DOM
结合代码和这两张图片我们可以来理一理Shadow DOM的结构
从外到内分别是shadow-host、shadow-root、shadow-tree我们可以来看看MDN对Shadow DOM的描述
Shadow host: The regular DOM node that the shadow DOM is attached to.
Shadow host是一个被Shadow DOM附着的普通DOM节点
Shadow tree: The DOM tree inside the shadow DOM.
Shadow tree是属于Shadow DOM下的节点树
Shadow root: The root node of the shadow tree.
Shadow root是Shadow tree的根节点
Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
Shadow boundary是Shadow DOM结束,普通DOM开始的分界线
同时我们可以看见shadow-tree的元素中出现了我们不熟悉的属性pseudo
何为pseudo
我们可能用过这样的方式来隐藏掉浏览器默认的丑陋滚动条
body::-webkit-scrollbar{display:none;}
对的,这个pseudo就是伪元素Pseudo Element
有了它,
我们可以在css中从shadow-host通过pseudo来访问shadow-root下的元素
Shadow DOM的好处
一个东西存在必然有它的价值,Shadow DOM为我们提供了简单有效的样式隔离,除此之外和普通DOM元素没有差别
在某个Shadow DOM内写的css代码并不会对该Shadow DOM以外的元素生效,于是我们可以通过Shadow DOM来隐藏控件的内部实现,就不用担心对样式表进行修改时对控件造成影响了
下面我们来看看Shadow DOM的简单使用
笔者找到两种方式来创建Shadow root
- 源自MDN的Element.attachShadow(shadowRootInit)
最后修改时间: 2020.01.08 - 源自简书帖子的Element.createShadowRoot()
最后修改时间: 2018.10.11
都可以通过来创建Element(Shadow host)下的Shadow root
笔者测试Element.attachShadow(shadowRootInit)在现代浏览器的兼容性更强
并且Element.createShadowRoot()不是W3C标准,MDN并不建议使用
于是以下均用Element.attachShadow(shadowRootInit)来举例子
对Element.createShadowRoot()有兴趣的可以跳转简书帖子阅读
详细说一说MDN的Element.attachShadow(shadowRootInit)
可以作为Shadow host使用attachShadow()的标签有
一个有合法名字的自定义标签、<article>、<aside>、<blockquote>、<body>、<div>、<footer>、<h1>、<h2>、<h3>、<h4>、<h5>、<h6>、<header>、<main>、<nav>、<p>、<section>、<span>
其中的shadowRootInit包括以下字段
mode
open
- 可以从根外部的JavaScript访问Shadow root的元素
- Element.shadowRoot; // Returns a ShadowRoot obj
closed
- 与open相反
- Element.shadowRoot; // Returns null
delegatesFocus
true
- 点击Shadow Root的某个不可focus元素时,会聚焦到另外第一个可以focus的元素上,并为Shadow host提供任何可用的:focus样式
false
- 与true相反
这是MDN提供的兼容图
下面我们用一些例子来帮助我们理解
比方说现在的HTML代码是这样的
<body>
<h1>outer h1</h1>
<div id='test'>
</div>
</body>
然后我们对#test添加Shadow DOM并且在Shadow DOM里添加css样式
if (document.body.attachShadow) { //做兼容性处理
let test = document.querySelector('#test');
let shadow = test.attachShadow({
mode: 'open'
});
shadow.innerHTML = `
<h1>inner h1</h1>
<style>
h1{color:red;text-align:center}
</style>
`;
}
效果图
代码图
可以看到Shadow DOM里的样式并没有泄露出来,这给我们创建Web Component奠定了基础