理解Shadow DOM

1. 什么是Shadow DOM?

Shadow DOM 如果按照英文翻译的话可以理解为 影子DOM, 何为影子DOM呢?可以理解为一般情况下使用肉眼看不到的DOM结构,那如果一般情况下看不到的话,那也就是说我们无法直接控制操纵的DOM结构。
Shadow DOM 它是HTML的一个规范,它允许在文档(document)渲染时插入一颗DOM元素子树,但是这个子树不在主DOM树中。
它允许浏览器开发者封装自己的HTML标签、css样式和特定的javascript代码、同时开发人员也可以创建类似 <input>、<video>、<audio>等、这样的自定义的一级标签。创建这些标签内容相关的API,可以被叫做 Web Component。

如上基本解释,让我们很难理解,因此我们可以先看下如下一个 input标签的demo吧。

html代码如下:

复制代码

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}input::-webkit-input-placeholder {color: red;}</style></head><body><div id="app"><input type="number" placeholder="请输入内容" /></div></body>
</html>

复制代码

然后我们在浏览器查看如下所示:

如上代码可以看到,我们使用 input::-webkit-input-placeholder{color: red;} 伪类后,input中的placeholder字体颜色发生改变了,但是我们的input元素的结构并没有看到伪类相关的html结构。

为了能看到基本结构,我们只需要在chrome浏览器中,打开开发者工具,点击右上角的 "Settings"按钮,勾选 "Show use agent shadow DOM". 后 如下图所示:

然后我们再来看下input元素的基本代码结构如下看到:

如上截图所示,我们可以看到 "请输入内容" 中的div元素上有一个属性为 pseudo="-webkit-input-placeholder", 因此我们使用 input元素的伪类 input::-webkit-input-placeholder{} 这样就可以控制元素的样式了。

2. ShadowDOM 存在的意义?

首先我们来看下Shadow-dom 基本的结构如下:

Document: 是document文档对象。
shadow-host: Shadow DOM的容器元素,即:它是Shadow DOM的一个宿主元素。比如:<input />、<audio>、<video> 标签;就是shadow-dom的宿主元素。
shadow-root: Shadow DOM的根节点。通过createShadowRoot返回的文档片段被称为 shadow-root, 它和它的后代元素,都会对用户隐藏。但是它会在浏览器中被渲染的,也就是说它是存在的,但是一般情况下在浏览器中是不显示出来的。在chrome浏览器中,如上我们可以看到的。
contents: Shadow DOM包含的子节点树结构。它包含 <input />、<audio>、<video>等标签中各子组件的DOM的具体实现。

那么ShadowDOM存在的意义是?

我们都知道像React或Vue这样的都有组件的概念,比如element-ui等这样的vue组件。但是我们常用的input、audio、video、等这些元素,其实它也是以组件的形式存在的,即:HTML Web Component. 即这些都有 Shadow DOM。

因此我们可以认为像 input, audio, video等这些元素也是以组件的形式存在的。那么这些组件内部是由一些HTML标签组成的。
这些元素组成了DOM树的子树。但是当我们使用input,audio,或video等这些元素组件的时候,都会知道该子树的结构,当我们访问网页DOM结构的时候,这些子树都会暴露出来,当我们使用css样式去改变DOM的样式的时候,如果DOM的类名和该子树的类名相同的话,会和子树的样式产生冲突,并且我们使用控件的时候,我们并不关心控件的内部结构,只关心控件本身,因此我们需要将控件的内部信息封装起来。因此 W3C提出了 ShadowDOM的概念,ShadowDOM可以使一些DOM节点在特定范围内可见,在网页中是不可见的。但是在页面渲染的时候也会渲染该ShadowDOM。
也可以看这篇文章介绍

ShadowDOM在各个浏览器的支持程度呢?

查看Can I Use(https://caniuse.com/#search=shadowDOM) 可以看到它在浏览器下的支持程度,如下所示:

3. 如何控制 shadow-dom?

既然W3C提出了ShadowDOM的概念,并且在网页中一般是不可见的,那么我们是否可以控制该 shadow-dom呢?
下面我们以 <video>标签来讲解吧,比如如下HTML代码:

复制代码

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><video src="http://www.w3school.com.cn/i/movie.ogg" controls="controls">your browser does not support the video tag</video></div></body>
</html>

复制代码

3.1 使用伪类来控制 shadow-dom的样式。

首先看如上代码显示的效果图如下:

在chrome浏览器下,我们查看 shadow-dom 结构,可以看到每个元素都加上了一个pesudo 这样的属性。我们可以通过这些属性使用伪类来控制他们的样式。如下图所示:

基本样式如下:

video::-webkit-media-controls-play-button {background-color: red;
}

我们给播放按钮添加了一个背景颜色为红色。

注意:很遗憾的是,只有chrome浏览器下支持,其他浏览器下并不支持,虽然大部分浏览器下支持shadow-dom。

4. 使用javascript如何来创建Shadow DOM

4.1 使用 createShadowRoot()来创建Shadow DOM。

如下基本代码:

复制代码

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}.shadow-child {color: red;}</style></head><body><div id="app"><div class="shadow-cls">hello, kongzhi</div></div><script type="text/javascript">// 1. 获取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 创建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// 3. shadow root 作为影子树的第一个节点,其他的节点,比如如下的p节点都是它的子节点。shadowRoot.innerHTML = '<p class="shadow-child">我是子节点</p>';</script></body>
</html>

复制代码

然后我们查看效果如下:

如上我们可以看到,给影子树子节点设置css样式,但是并没有生效,那是因为影子宿主(shadow host)和影子根(shadow root)之间存在影子边界。影子边界保证DOM编写的css和javascript代码都不会影响到ShadowDOM.当然反之也是一样,互不影响。

4.2 理解<content> 和 <template> 的用法。

<content>标签可以把来自DOM文档的内容添加到shadow DOM的内容被叫做分布节点。

<content>有一个select属性来告诉<content>标签要插入的内容,select属性值是一个使用CSS选择器来获取想要的内容。
选择器包括类选择器、元素选择器等。

比如如下代码:

复制代码

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><div class="shadow-cls"><em class="shadowhost-content1">我是空智</em><em class="shadowhost-content2">我是龙恩</em></div><!-- 下面是一个模板 template --><template class="template"><div><h1>哈哈,</h1><content select=".shadowhost-content1"></content>我来了<content select=".shadowhost-content2"></content>!</div></template></div><script type="text/javascript">// 1. 获取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 创建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// 3. 获取模板元素var template = document.querySelector('.template');/*4. template.content 会返回一个文档片段。5. 使用 document.importNode获取节点,true参数表示深度克隆*/shadowRoot.appendChild(document.importNode(template.content, true));</script></body>
</html>

复制代码

执行结果如下:

我们也可以通过如下打印:

console.log(template.innerHTML);   // 获取完整的HTML片段
console.log(template.content);  // 返回一个文档片段#document-fragment
console.log(template.childNodes);  // 返回[],说明childNodes无效

这些的信息的区别,如下所示:

4.3 shadowDOM样式

1. 宿主样式:
在shadow DOM中利用 :host定义宿主的样式。:host 是伪类选择器,给所有的宿主添加样式可以如下代码::host 或 :host(*); 如果想给单独的宿主添加样式可以 :host(xx); 其中xx是宿主的标签或类选择器等。并且:host还可以配合 :hover、:active等状态来设置样式,如下代码:

复制代码

/* 定义宿主样式 :host */
:host {color: red;
}
/* 定义宿主hover状态下的样式 */
:host(:hover) {color: black;
}

复制代码

2. ::shadow

影子边界为了保证DOM编写的css或javascript不影响到shadowDOM。如果我们想让 shadowDOM添加一些样式,可以使用 ::shadow这样的。

3. /deep/

::shadow选择器有一个缺陷是它只能穿透一层影子边界,如果我们在一个影子树中嵌套了多个影子树的话,那么我们需要使用/deep/ 这样的来编写css样式。

4. ::content

我们通过 <content> 标签把主文档中的元素添加到shadowDOM的内容被叫做分布节点。分布节点的样式渲染需要用到::content。比如我们想给节点为em标签的话,我们直接写 em {} 这样的是不会生效的,我们要写成 ::content > em {}. 比如如下代码:

/* 分布节点的样式渲染需要用到 ::content */
::content > em {color: blue;background: red;
}

下面我们再来看一下如下demo。

复制代码

<!DOCTYPE html>
<html><head><title>Shadow DOM</title><style>#app {margin: 100px;}</style></head><body><div id="app"><div class="shadow-cls"><em class="shadowhost-content1">我是空智</em><em class="shadowhost-content2">我是龙恩</em></div><!-- 下面是一个模板 template --><template class="template"><style>/* 定义宿主样式 :host */:host {color: red;}/* 定义宿主hover状态下的样式 */:host(:hover) {color: black;}/* 分布节点的样式渲染需要用到 ::content */::content > em {color: blue;background: red;}</style><div><h1>哈哈,</h1><content select=".shadowhost-content1"></content>我来了<content select=".shadowhost-content2"></content>!</div></template></div><script type="text/javascript">// 1. 获取影子宿主 shadow hostvar shadowHost = document.querySelector('.shadow-cls');// 2. 创建影子 shadow rootvar shadowRoot = shadowHost.createShadowRoot();// var shadowRoot = shadowHost.attachShadow({mode: 'open'});// 3. 获取模板元素var template = document.querySelector('.template');/*4. template.content 会返回一个文档片段。5. 使用 document.importNode获取节点,true参数表示深度克隆*/shadowRoot.appendChild(document.importNode(template.content, true));</script></body>
</html>

复制代码

然后我们再在chrome浏览器下看下结果如下所示:

注意:上面的demo在chrome浏览器下会生效,在firefox或safari是不支持的。因此如果在平时的开发中我们需要引入对应的库进来才会支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

046 实例11-自动轨迹绘制

目录 一、"自动轨迹绘制"问题分析1.1 问题分析1.2 自动轨迹绘制二、"自动轨迹绘制"实例讲解2.1 自动轨迹绘制2.2 数据接口定义2.3 数据文件三、"自动轨迹绘制"举一反三3.1 理解方法思维3.2 应用问题的扩展一、"自动轨迹绘制"问题分析 …

bootstrap-select采坑

bootstrap-select采坑 1.class"selectpicker" 普通的下拉框功能 2.title"请选择城市名称" title的作用与palcehoder一样。 3.select class"selectpicker" multiple selectpicker和multiple属性的搭配使用可实现多选 4.data-live-search"tru…

对vue虚拟dom的研究

Vue.js通过编译将template 模板转换成渲染函数(render ) &#xff0c;执行渲染函数就可以得到一个虚拟节点树在对 Model 进行操作的时候&#xff0c;会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比…

element-ui之dialog组件title插槽的使用

dialog对话框组件title属性的slot使用方法 使用背景 需要单独控制title中某个数据显示及样式&#xff0c;footer也一样 <el-dialog// 也可以这样写,但是没有办法单独控制name age的显示// title"name age"title"提示":visible.sync"dialogVisi…

css3自适应布局单位vw,vh

视口单位(Viewport units) 什么是视口&#xff1f; 在桌面端&#xff0c;视口指的是在桌面端&#xff0c;指的是浏览器的可视区域&#xff1b;而在移动端&#xff0c;它涉及3个视口&#xff1a;Layout Viewport&#xff08;布局视口&#xff09;&#xff0c;Visual Viewport…

python 操作 elasticsearch-7.0.2 遇到的问题

错误一&#xff1a;TypeError: search() got an unexpected keyword argument doc_type&#xff0c;得到不预期外的参数 解决方法&#xff1a;elasticsearch7里不用文档类型&#xff0c;所以去掉 doc_typecredit_data 错误二&#xff1a;RequestError(400, illegal_argument_ex…

用到的Shell

sed 1i 添加的内容 file #这是在第一行前添加字符串 sed $i 添加的内容 file #这是在最后一行行前添加字符串 sed $a添加的内容 file #这是在最后一行行后添加字符串 sed -i s/.*/行首添加内容&行尾添加内容/ 文件名 //每一行 sed -i $a新增的一行 tars_build_tar.sh a…

如何解决浏览器缩小出现横向滚动条时网页背景图出现空白的问题

原因&#xff1a; 当窗口缩小时&#xff0c;浏览器默认100%宽度为浏览器窗口的宽度。而忽略了下部内容层固定宽度(1024px)。从而出现了固定宽度大于100%宽度的现象。浏览以此理解来解析页面&#xff0c;就出现了容器宽度理解上的差异&#xff0c;出现了一个非常奇特的BUG。 解…

前端设计模式

1. 单例模式 2.装饰器模式 转载于:https://www.cnblogs.com/lyraLee/p/11210985.html

区别 (function($){...})(jQuery)、$(function(){ })和$.fn

一、(function($){…})(jQuery) 首先function(arg){...}定义了一个匿名函数&#xff0c;参数为arg,而调用时需要在函数后面写上括号和实参&#xff0c;由于操作符的优先级&#xff0c;函数本身也需要括号&#xff0c;也就成了&#xff1a; &#xff08;function(arg){...}&…

git 清除缓存

清除git缓存 git config --local --unset credential.helpergit config --global --unset credential.helpergit config --system --unset credential.helper保存git缓存 git config --global credential.helper store转载于:https://www.cnblogs.com/zhouyideboke/p/11211650.…

网页里如何使用js禁用控制台

网页里如何禁用右击事件&#xff1f;使用jQuery&#xff0c;几句代码就可以搞定了 document.oncontextmenu function(){return false;} 简单示例&#xff1a; js实现&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><…

Java生鲜电商平台-订单中心服务架构与异常订单逻辑

Java生鲜电商平台-订单中心服务架构与异常订单逻辑 订单架构实战中阐述了订单系统的重要性&#xff0c;并从订单系统的信息架构和流程上对订单系统有了总体认知&#xff0c;同时还穿插着一些常见的订单业务规则和逻辑。上文写到订单的拆单部分时搁置了&#xff0c;现在接上文继…

Vuex的全面用法总结

1. vuex简介 vuex是专门用来管理vue.js应用程序中状态的一个插件。他的作用是将应用中的所有状态都放在一起&#xff0c;集中式来管理。需要声明的是&#xff0c;这里所说的状态指的是vue组件中data里面的属性。了解vue的同学应该是明白data是怎么回事的吧&#xff0c;如果不懂…

vue中通过第三方代理解决跨域问题

最近在学node&#xff0c;遇到了跨域的问题&#xff0c;来记录下方法 首页服务端的框架是通过express-generator 搭建起来的 npm install -g express-generator 具体接下来的细节不多说&#xff0c;今天主要说跨域 的问题 左侧为服务端项目结构&#xff0c;www为可执行文件&am…

使用V-chart时配置踩过的一些坑

如何配置图表信息 echart的配置项可谓是相当的海量&#xff0c;能不看就不看。而v-chart对其进行了不少的简化&#xff0c;所以我们想要自定义一个图表时&#xff0c;最好按照以下步骤来检查&#xff1a; 图表私有属性 v-chart每一个图表都有自己独有的设置项&#xff0c;想…

ZDOzMRVAOq

11 转载于:https://www.cnblogs.com/wc529065/p/11212226.html

vue 出现Elements in iteration expect to have 'v-bind:key' directives

是由于eslint检测出现bug 解决方法有两种 v-for 后添加 :keyitem <li v-for"item in list" :key"item"> 在build处关闭eslint检测 ...(config.dev.useEslint ? [createLintingRule()] : []),

requestAnimationFram

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画&#xff0c;并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数&#xff0c;该回调函数会在浏览器下一次重绘之前执行 注意&#xff1a;若你想在浏览器下次重绘之…

vue/return-in-computed-property Enforce that a return statement is present in computed property

此规则强制return语句在computed属性中得完整存在。 <script> export default {computed: {/* ✓ GOOD */foo () {if (this.bar) {return this.baz} else {return this.baf}},bar: function () {return false},/* ✗ BAD */baz () {if (this.baf) {return this.baf}},ba…