Web Components详解-Shadow DOM基础

news2024/12/25 12:29:27

目录

引言

概念

基本用法

attachShadow函数

mode(模式)

delegatesFocus(委托聚焦)

Custom Elements+Shadow DOM

基本用法

样式及属性隔离

写在最后

相关代码

参考文章


引言

上篇文章的自定义标签中,我们使用customElements对象对原生标签进行拓展,达到组件的拓展性与复用性的效果,那么如何保证组件的属性、结构及样式的封装隔离便是本篇文章将要分享的内容,本篇文章不仅仅会介绍Shadow DOM的基本用法,还会对前面说到的Custom Elements做一个使用场景的拓展

概念

在JS作用域一文中,我们提到全局作用域和局部作用域的概念,如果全局作用域没有处理好可能会导致作用域污染,出现问题。如果单纯的使用Custom Elements实现自定义组件的功能可能会存在样式冲突,Dom干扰,组件中的类名或者ID冲突,复用性复杂等问题,这会导致组件的可靠性和可维护性降低,此时一个独立的Dom容器就显得非常重要了。

影子Dom(Shadow DOM),可以理解为DOM中的DOM,它是前端组件的核心之一,它提供了一套将标签与DOM树隔离的机制,大大提升了自定义标签或者普通标签的封装性,可重用性和可维护性。

  

在使用Shadow DOM前,我们先了解一些专有名词

Shadow Host(影子宿主):Shadow DOM的容器。它是普通DOM中的一个元素,可以称为宿主元素。宿主元素可以是自定义的Web组件,例如一个自定义的标签、视频标签或任何其他的自定义元素。

Shadow Tree(影子的树):Shadow DOM内部的DOM树。在Shadow Host内部,开发者可以定义一个独立的DOM子树,其中包含组件的样式和结构,这个内部DOM树就是Shadow Tree。

Shadow Root(影子的根节点):Shadow Tree的根节点。通过使用Shadow Root,我们可以将Shadow Tree连接到Shadow Host上。影子根是Shadow DOM的入口点,通过Shadow Root我们可以访问Shadow Tree中的内容。

Shadow Boundary(影子的边界):Shadow DOM的隔离边界。Shadow DOM结束的地方,也是常规DOM开始的地方。这个边界将Shadow DOM和外部DOM分隔开来,防止它们互相干扰。外部DOM无法直接访问Shadow Tree的内容,只能访问到Shadow Host。

基本用法

使用element.attachShadow函数在宿主(Host)标签上创建根(Root)元素

<body>
    <div id="host"></div>
    <script>
        const hostEle = document.querySelector("#host")
        const treeEle = document.createElement("span")// 影子的树或后代节点
        treeEle.textContent = "treeEle"
        const rootEle = hostEle.attachShadow({ mode: 'open' });// 创建Shadow Root
        rootEle.appendChild(treeEle)// 将树添加到root标签
    </script>
</body>

attachShadow函数

attachShadow函数可以传入一个ShadowRootInit类型的参数,它有三个属性,分别是:mode,delegatesFocus,slotAssignment。

mode(模式)

mode是必传的值,代表是否允许外部访问和修改ShadowRoot的属性,open表示开放(允许),closed表示关闭(不允许)。

const hostEle = document.querySelector("#host")
const treeEle = document.createElement("span")
treeEle.textContent = "treeEle"
const rootEle = hostEle.attachShadow({ mode: 'closed' });// 不允许外部访问
rootEle.appendChild(treeEle)
console.log(hostEle.shadowRoot);// null

delegatesFocus(委托聚焦)

delegatesFocus可选,传入布尔值,表示是否减轻自定义元素的聚焦性能问题。当我们定义自定义标签时,标签可能不支持聚焦,此时让第一个可聚焦的部分成为焦点, 并且shadow host将提供所有可用的 :focus样式。怎么理解这句话?参考了许多网上的文章,做个总结,思考下面的代码:

<body>
    <div id="host"></div>
    <script>
        const hostEle = document.querySelector("#host")// 宿主元素
        const rootEle = hostEle.attachShadow({ mode: 'open', delegatesFocus: true });// 根元素
        const styleEle = document.createElement("style")// 样式元素
        styleEle.textContent = `div {
        width: 500px;
        height: 80px;
        border: 1px solid #000;
    }

    input:focus {
        background: lightblue;
    }`
        // 内部的其他树标签
        const treeEle = `<div>
        <input type="text" />
    </div>`
        // 在Shadow DOM中添加全部元素
        rootEle.appendChild(styleEle)
        rootEle.innerHTML += treeEle

    </script>
</body>

当delegatesFocus设置为true,并点击外部的div时,内部的输入框会被聚焦,输入框是标签中可以被聚焦的元素;

当delegatesFocus设置为false,点击外部div就不会聚焦任何标签。

Custom Elements+Shadow DOM

基本用法

基于自定义标签和上面讲到的知识点,我们尝试将二者结合,实现一个简易的自定义组件,思考下面的代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <my-custom-element></my-custom-element>
    <script>
        const elemName = "my-custom-element"
        const ele = document.querySelector(elemName)
        class MyCustomElement extends HTMLElement {
            constructor() {
                super();
                this.initShadow()
            }
            // 基于当前自定义标签创建
            initShadow() {
                this.appendChild(this.attachShadow({
                    mode: "open"
                }))
                this.shadowRoot.innerHTML = "<div>hello world</div>"
            }
        }
        customElements.define(elemName, MyCustomElement)
    </script>
</body>

</html>

样式及属性隔离

思考以下代码,通过props我们可以将组件的行为及数据传入自定义标签中,通过attachShadow隔离样式,自定义标签隔离属性和行为,达到一个组件容器的效果,其中this.shadowRoot就是标签的影子根元素,即this.attachShadow的产物

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
    <style>
        div {
            /* 全局样式无法影响内部标签 */
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <my-custom-element></my-custom-element>
    <script>
        const elemName = "my-custom-element"
        const ele = document.querySelector(elemName)
        class MyCustomElement extends HTMLElement {
            styleContent = ""// 组件局部样式
            treeEle = ""// 影子(树)子节点
            constructor() {
                super();
                this.initProps()
                this.initShadow()
            }
            // 通过props传入参数
            initProps() {
                const { ele, style } = this.props
                this.styleContent = style
                this.treeEle = ele
            }
            // 基于当前自定义标签创建
            initShadow() {
                this.appendChild(this.attachShadow({
                    mode: "open"
                }))
                const styleEle = document.createElement("style")
                styleEle.textContent = this.styleContent
                this.shadowRoot.appendChild(styleEle)
                this.shadowRoot.innerHTML += this.treeEle
            }
        }
        // 自定义标签参数
        ele.props = {
            ele: "<div>hello world</div>",
            style: `
            div {
                width: 100px;
                height: 100px;
                background: lightcoral;
            }
            `
        }
        customElements.define(elemName, MyCustomElement)
        console.log(document.querySelectorAll("div")); // []  全局无法获取shadow中的标签
        console.log(ele.shadowRoot.querySelectorAll("div")[0].textContent);// hello world    可以通过element获取shadow标签
    </script>
</body>

</html>

上述代码可以看到,全局样式与元素只在当前的shadowDOM中可以访问,与全局作用域形成了隔离关系

写在最后

本文结合Custom Elements和Shadow DOM实现了一个简易的自定义组件,通过使用props传递参数,将组件的行为及数据传入自定义标签中,并通过attachShadow隔离样式,实现了属性、结构和样式的封装隔离,从而达到组件容器的效果。那么自定义组件之间如何进行通信?组件样式及选择器如何使用?在后续的系列文章中,我会对这些知识点做个详细说明,敬请期待!

感谢你看到了最后,如果觉得文章还不错的话,还请给个三连,感谢!

相关代码

myCode: 基于js的一些小案例或者项目 - Gitee.com

参考文章

影子 DOM(Shadow DOM)

使用 shadow DOM - Web API 接口参考 | MDN

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

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

相关文章

iWatch框架设计

iWatch框架设计 一、项目框架结构设计 1、项目文件介绍 OverSeaProject&#xff1a;是IOS相关文件文件内容iWatchApp和iWatch Extension&#xff1a;是之前使用xcode14之前的xcode创建的360 app的Watch App&#xff0c;产生的文件结构&#xff0c;包含一个app和Extension的ta…

leetcode 1609.奇偶树

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;奇偶树 思路&#xff1a; 树的层序遍历&#xff0c;用队列辅助。用一个变量记录当前是多少层&#xff0c;以及当前层的节点个数&#xff0c;依次遍历&#xff0c;因为需要判断当前层是否严格递增或递减&#xff0c;如果正…

突破销售瓶颈:亚马逊卖家如何借力TikTok网红营销?

随着社交媒体的崛起&#xff0c;营销方式也在不断变革。TikTok作为一款风靡全球的短视频平台&#xff0c;吸引了数以亿计的用户&#xff0c;成为了品牌宣传和销售的新热点。对于亚马逊卖家而言&#xff0c;通过合理运用TikTok网红营销策略&#xff0c;可以有效提升产品的曝光度…

微信最新更新隐私策略(2023-08-15)

1、manifest.json 配置修改 在mp-weixin: 参数修改&#xff08;没有就添加&#xff09; "__usePrivacyCheck__": true, ***2、注意 微信开发者工具调整 不然一直报错 找不到 getPrivacySetting 废话不多说 上代码 3、 编辑首页 或者用户授权界面 <uni-popup…

无涯教程-JavaScript - DMAX函数

描述 DMAX函数返回列表或数据库中符合您指定条件的列中的最大数字。 语法 DMAX (database, field, criteria)争论 Argument描述Required/Optionaldatabase 组成列表或数据库的单元格范围。 数据库是相关数据的列表,其中相关信息的行是记录,数据的列是字段。列表的第一行包含…

GPT-5继续秘密训练中!ChatGPT开学大礼包

&#x1f989; AI新闻 &#x1f680; GPT-5继续秘密训练中&#xff01;DeepMind联合创始人披露了未来模型的规模增长 摘要&#xff1a;DeepMind联合创始人在采访中透露&#xff0c;OpenAI正在秘密训练GPT-5&#xff0c;未来3年&#xff0c;Inflection模型将比现在的GPT-4大10…

Python While 循环语句

Python 编程中 while 语句用于循环执行程序&#xff0c;即在某条件下&#xff0c;循环执行某段程序&#xff0c;以处理需要重复处理的相同任务。其基本形式为&#xff1a; while 判断条件(condition)&#xff1a;执行语句(statements)…… 执行语句可以是单个语句或语句块。判…

BL110设备支持Modbus TCP协议接入

随着物联网技术的不断发展&#xff0c;越来越多的工业设备被连接到云平台上&#xff0c;以实现远程监控和管理。在这篇文章中&#xff0c;我们将介绍如何方便用户快速把多种工业设备接入几个主流的云平台&#xff0c;如华为云 IoT、AWS IoT、阿里云 IoT、ThingsBoard、金鸽云等…

【多线程案例】线程池的应用及实现

文章目录 1. 什么是线程池&#xff1f;2. 线程池的应用3. 自己实现线程池 1. 什么是线程池&#xff1f; 线程池就相当于一个池子&#xff0c;里面有很多的创建好的线程&#xff0c;可以进行统一的管理。当我们使用线程的时候&#xff0c;直接从里面调取&#xff0c;而不用在频繁…

如何将Word转成PDF?试一下这个转换方法

Word转成PDF是现代办公中常见的需求&#xff0c;它可以确保文件的格式和内容在不同平台上保持一致&#xff0c;并且更加方便共享和打印。在这个数字化时代&#xff0c;我们经常需要将Word文档转换为PDF格式&#xff0c;无论是个人用户还是商务用户都会遇到这样的需求。那么如何…

接口自动化测试中如何对xml 格式做断言验证?

在服务端自动化测试过程中&#xff0c;发起请求之后还需要对响应值进行验证&#xff0c;验证响应信息符合预期值之后&#xff0c;这一条接口自动化测试用例才算完整的通过。所以这一章节&#xff0c;将会讲解在接口自动化测试中&#xff0c;是如何对服务端返回的 XML 格式响应内…

4个维度讲透ChatGPT技术原理,揭开ChatGPT神秘技术黑盒【文末送书】

文章目录 写在前面1.Tansformer架构模型2. ChatGPT原理3. 提示学习与大模型能力的涌现4. 行业参考建议写作末尾 写在前面 2022年11月30日&#xff0c;ChatGPT模型问世后&#xff0c;立刻在全球范围内掀起了轩然大波。无论AI从业者还是非从业者&#xff0c;都在热议ChatGPT极具…

一种基于Python的自定义日志解析的实践方法

需求说明&#xff1a; 从如下的日志文件里解析出如下字段&#xff1a; 参数名&#xff1a;教育程度 左值&#xff1a;60 右值&#xff1a;90 表达式&#xff1a;等于 结果&#xff1a;不满足 解决方法&#xff1a; Step1: 因为原始日志来源于网页&#xff0c;这里真正的…

前端加密方式

前端加密 1.不可逆加密2.可逆加密a.对称加密b.非对称加密&#xff08;本文重点&#xff09;a.含义&#xff1a;b.过程理解&#xff1a;c.项目中使用&#xff1a; 总结&#xff1a;参考地址 目前搜索前端加密是可以看到有非常非常多的方法的&#xff0c;这里我们需要对其分类总结…

设计模式-9--迭代器模式(Iterator Pattern)

一、什么是迭代器模式 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;用于提供一种统一的方式来访问一个聚合对象中的各个元素&#xff0c;而不需要暴露该聚合对象的内部结构。迭代器模式将遍历集合的责任从集合对象中分离出来&#xf…

睿思BI实现杜邦分析

杜邦分析法&#xff08;DuPont analysis&#xff09;是一种分析企业财务状况的方法&#xff0c;得名于美国杜邦公司。该方法可以应用于销售业绩分析。 睿思BI实现杜邦分析效果如下&#xff1a; 效果演示地址&#xff1a;https://www.ruisitech.com/rsbi-ultimate/#/dashboard/…

Hook技术

Hook 英文直译是“钩子”的意思&#xff0c;在程序中将其理解为“劫持”更好理解&#xff0c;意思是&#xff0c;通过 Hook 技术来劫持某个对象&#xff0c;从而控制它与其它对象的交互。 Hook 技术是一种用于改变 API 执行结果的技术&#xff0c;Android 系统中有一套自己的事…

架构设计基础设施保障IaaS存储

目录 1. 云硬盘2. 对象存储3. 表单上传案例4. 服务上传验证5. 云数据库6. 云数据库操作7. 服务连接云数据库8. 新一代原生数据库9 阿里云PolarDB生产最佳实践 1. 云硬盘 HDD&#xff08;普通云盘&#xff09; 特征&#xff1a; 性能一般&#xff0c; IOPS大概在数百左右。 应…

iPhone 15 Pro展示设计:7项全新变化呈现

我们不应该再等iPhone 15 Pro在苹果9月12日的“Wonderlust”活动上发布了&#xff0c;而且可能会有很多升级。有传言称&#xff0c;iPhone 15 Pro将是自iPhone X以来最大的飞跃&#xff0c;这要归功于大量的新变化&#xff0c;从带有更薄边框的新钛框架到顶级A17仿生芯片和动作…

通过Siri打造智能爬虫助手:捕获与解析结构化数据

在信息时代&#xff0c;我们经常需要从互联网上获取大量的结构化数据。然而&#xff0c;传统的网络爬虫往往需要编写复杂代码和规则来实现数据采集和解析。如今&#xff0c;在苹果公司提供的语音助手Siri中有一个强大功能可以帮助我们轻松完成这项任务——通过使用自定义指令、…