0

0

使用自定义标签和 Shadow DOM 增强 HTML

WBOY

WBOY

发布时间:2023-08-29 09:37:07

|

1415人浏览过

|

来源于php中文网

原创

使用自定义标签和 shadow dom 增强 html

在上一篇文章中,我解释了创建自定义标签的基础知识。事实上,自定义标签消除了构建优秀 Web 应用程序时的一些脆弱性。然而,对控制的追求并没有停止,传统的自定义标签不足以构建性能丰富的应用程序。例如,代码中样式选择器的数量可能会随着自定义标签的增加而增加。这只是可能导致性能问题的众多因素之一。

解决此问题的一种方法是通过 Shadow DOM。

Shadow DOM 通过引入作用域样式来工作。它不需要任何特殊的命名约定或工具。使用 Shadow DOM,将 CSS 与标记捆绑起来变得很简单。此外,此功能允许我们隐藏有关普通 JavaScript 中实现的所有细节。

为什么使用 Shadow DOM?

Shadow DOM 提供以下解决方案:

立即学习前端免费学习笔记(深入)”;

  • 允许 DOM 中的独立元素。诸如 document.querySelector 之类的查询不会返回孤立的元素。
  • 允许作用域 CSS。作用域 CSS 确保所有样式规则都保留在页面内。它还意味着更简单的 CSS 选择器,没有任何命名冲突和许多通用类。

我们的示例

为了演示 Shadow DOM,我们将使用一个名为 tuts-tabs 的简单组件。本文中的所有引用都将指向这段代码。要体验 Shadow DOM,请看下面的演示:

了解 Shadow DOM

什么是 Shadow DOM?

开始使用 Shadow DOM 进行编码之前,您需要了解常规 DOM。

HTML 是网站的支柱。只需几分钟,您就可以创建一个页面。当您在浏览器中打开该页面时,DOM 开始发挥作用。一旦浏览器加载页面,它就会开始将 HTML 解析为数据模型。该数据模型是一个树结构,其中的节点代表 HTML 中的元素。该数据模型很容易用代码修改和操作。

缺点是整个网页甚至复杂的 Web 应用程序都会被视为单个数据结构。这不太容易调试!例如,适用于一个组件的 CSS 样式最终可能会影响应用程序中其他位置的另一个组件。

当您想要将界面的一部分与其余部分隔离时,可以使用 iframes。但 iframe 很重并且限制性极大。

这就是引入 Shadow DOM 的原因。它是现代浏览器的一项强大功能,允许 Web 开发人员在 DOM 中包含各种元素的子树。 DOM 的这些子树不会影响主文档树。从技术上讲,它们被称为影子树

影子树有一个影子根,它附加到 DOM 中的父级。该父级称为影子主机

例如,如果您将 <input type="range"> 插入由 WebKit 提供支持的浏览器,它将转换为滑块。为什么?这是一个滑块,因为子树 DOM 元素之一了解“范围”以更改其外观并引入类似滑块的功能。这是 Shadow DOM 给选项卡带来的一个优势。

哇,这是很多理论。现在,您可能想要编写一些代码来了解如何实现 Shadow DOM。

使用 Shadow DOM 的分步指南

第 1 步:创建 Shadow DOM 元素

使用 element.attachShadow() 创建 Shadow DOM 元素。

在我们的示例 tuts-tab 中,您将看到用于创建 Shadow DOM 元素的代码。

 let shadowRoot = this.attachShadow({mode: 'open'});

第 2 步. 将内容添加到 Shadow Root

接下来,我们将使用 .innerHTML 将内容添加到影子根目录。请注意,这不是填充 Shadow DOM 的唯一方法。有许多 API 可以帮助您填充 Shadow DOM。

shadowRoot.innerHTML = ``

第 3 步:将自定义元素连接到 Shadow DOM

将自定义元素连接到 Shadow DOM 非常简单。请记住,当您将自定义元素与 Shadow DOM 组合时,您将能够使用 CSS、JavaScript 和 HTML 创建封装组件。因此,您将创建一个可以在您的应用程序中重用的全新 Web 组件。

在我们的示例中,我们使用 customElements.define() 创建一个新的自定义元素。正如上一教程中提到的,新元素的名称中应包含“-”。 tuts-tabs 组件扩展了 HTMLElement

当我们扩展 HTMLElement 时,在构造函数中调用 super() 非常重要。另外,构造函数是需要创建 shadowRoot 的地方。

创想C2C多用户商城系统
创想C2C多用户商城系统

创想C2C商城系统,系统功能仿照淘宝设计,采用模块标签技术和静态html生成技术 基于Asp.Net/C#+SQL的开发的创想多用户商城系统,具有智能化、高扩展、稳定安全等特性,后台可自由添加频道,自由修改界面风格,商品无限级 分类,支持在线支付整合,通过安装和使用创想C2C商城系统,就可以轻松建立起专业大型的网上交易平台。创想C2C多用户商城系统5.6.3.8版本升级功能1.网站地区设置功能的增

下载
customElements.define('tuts-tabs', class extends HTMLElement {
    constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to <tuts-tabs>.
    const shadowRoot = this.attachShadow({mode: 'open'});
    ...
});

创建 shadowRoot 后,您可以为其创建 CSS 规则。 CSS 规则可以包含在 <style></style> 标记中,并且这些样式的范围仅限于 tuts-tab

customElements.define('tuts-tabs', class extends HTMLElement {
    constructor() {
    super(); 
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
         <!-- styles are scoped to tuts-tabs! -->
         <style>#tabs { ... }</style>
    `;
    }
    ...
});

第 4 步:向 Shadow DOM 添加样式

tuts-tab相关的CSS可以写在<style></style>标签内。请记住,此处声明的所有样式都将作用于 tuts-tab Web 组件。 作用域 CSS 是 Shadow DOM 的一项有用功能,它具有以下属性:

  • CSS 选择器不会影响 Shadow DOM 之外的组件。
  • Shadow DOM 中的元素不受其外部选择器的影响。
  • 样式的范围仅限于宿主元素。

如果你想在 Shadow DOM 中选择自定义元素,你可以使用 :host 伪类。当 :host 伪类用于普通 DOM 结构时,它不会产生任何影响。但在 Shadow DOM 内部,它会产生很大的差异。您将在 tuts-tab 组件中找到以下 :host 样式。它决定显示和字体样式。这只是一个简单的示例,展示如何将 :host 合并到 Shadow DOM 中。

:host 的一个问题是它的特殊性。如果父页面有 :host,它将具有更高的特异性。父样式内的所有样式都会获胜。这是从外部覆盖自定义元素内部样式的一种方法。

 :host {
  display: inline-block;
  width: 650px;
  font-family: 'Roboto Slab';
  contain: content;
}

随着 CSS 变得更简单,Shadow DOM 的整体效率也会提高。

下面定义的所有样式都是影子根的本地样式。

shadowRoot.innerHTML = `
<style>
:host {
  display: inline-block;
  width: 650px;
  font-family: 'Roboto Slab';
  contain: content;
}
#panels {
  box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
  background: white;
  border-radius: 3px;
  padding: 16px;
  height: 250px;
  overflow: auto;
}
#tabs slot {
  display: inline-flex; /* Safari bug. Treats <slot> as a parent */
}
...
</style>

同样,您可以自由地在 Shadow DOM 中引入样式表。当您在 Shadow DOM 内链接样式表时,它们的作用域将位于 Shadow 树内。这是一个简单的例子来帮助您理解这个概念。

shadowRoot.innerHTML = `
<style>
:host {
  display: inline-block;
  width: 650px;
  font-family: 'Roboto Slab';
  contain: content;
}
#panels {
  box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
  background: white;
  border-radius: 3px;
  padding: 16px;
  height: 250px;
  overflow: auto;
}
...
</style>
<link rel="stylesheet" href="styles.css">
...

步骤 5. 在自定义组件中定义 HTML 元素

接下来,我们可以定义 tuts-tab 的 HTML 元素。

在简单的选项卡结构中,应该有可单击的标题和反映所选标题内容的面板。这显然意味着我们的自定义元素应该有一个带有标题的 div 和一个用于面板的 div 。 HTML 组件将定义如下:

customElements.define('tuts-tabs', class extends HTMLElement {
    constructor() {
    super(); const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
        <style>#tabs { ... }</style>
        
        ....
        
        // Our HTML elements for tuts-tab
        <div id="tabs">...</div>
        <div id="panels">...</div>
        ...
    `;
    }
    ...
});

在面板的 div 中,您会遇到一个有趣的标签,名为 <slot></slot>。我们的下一步是了解有关插槽的更多信息。

第 6 步:在 Shadow DOM 中使用槽

Slot 在 Shadow DOM API 中起着至关重要的作用。插槽充当自定义组件内的占位符。这些组件可以由您自己的标记填充。槽声明分为三种不同类型:

  1. 您可以拥有零个插槽的组件。
  2. 您可以创建一个包含后备内容或空内容的槽位。
  3. 您可以使用整个 DOM 树创建槽。

在我们的 tuts-tabs 中,我们有一个用于选项卡标题的命名槽,以及另一个用于面板的槽。命名槽会创建您可以通过名称引用的孔。

customElements.define('tuts-tabs', class extends HTMLElement {
    constructor() {
    super(); const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
        <style>#tabs { ... }</style>
        
        ....
        
        // Our HTML elements for tuts-tab
        <div id="tabs">
            <slot id="tabsSlot" name="title"></slot>
         </div>
        <div id="panels">
            <slot id="panelsSlot"></slot>
        </div>
        ...
    `;
    }
    ...
});

第 7 步:填充插槽

现在,是时候填充插槽了。在之前的教程中,我们了解了定义自定义元素的四种不同方法,并且 tuts-tabs 使用其中两种方法来构建选项卡:connectedCallbackdisconnectedCallback

connectedCallback 中,我们将填充步骤 6 中定义的槽。我们的 connectedCallback 将定义如下。我们使用 querySelector 来识别 tabsSlotpanelsSlot。当然,这并不是识别 HTML 中插槽的唯一方法。

识别槽后,您需要为其分配节点。在 tuts-tab 中,我们使用以下 tabsSlot.assignedNodes 来标识选项卡的数量。

connectedCallback() {
    ...
    const tabsSlot = this.shadowRoot.querySelector('#tabsSlot');
    const panelsSlot = this.shadowRoot.querySelector('#panelsSlot');

    this.tabs = tabsSlot.assignedNodes({flatten: true});
    this.panels = panelsSlot.assignedNodes({flatten: true}).filter(el => {
      return el.nodeType === Node.ELEMENT_NODE;
    });
    ...
  }

此外,connectedCallback 是我们注册所有事件监听器的地方。每当用户单击选项卡标题时,面板的内容都需要更改。可以在 connectedCallback 函数中注册用于实现此目的的事件侦听器。

第 8 步:实现逻辑和交互性

我们不会深入探讨如何实现选项卡及其功能的逻辑。但是,请记住,我们的自定义 tuts-tab 组件中实现了以下方法,用于在选项卡之间切换:

  1. onTitleClick 该方法捕获选项卡标题上的点击事件。它有助于在选项卡面板内切换内容。
  2. selectTab该函数负责隐藏面板和显示右侧面板。此外,它还负责突出显示所选的选项卡。
  3. findFirstSelected此方法用于在第一次加载时选择选项卡。
  4. selected这是一个用于获取所选选项卡的 getter 和 setter。

第 9 步.定义生命周期方法

继续,不要忘记定义 disconnectedCallback。这是自定义元素中的生命周期方法。当自定义元素从视图中销毁时,会触发此回调。这是在应用程序中删除操作侦听器和重置控件的最佳位置之一。但是,回调的范围仅限于自定义元素。在我们的例子中,它将是 tuts-tab

第 10 步。使用新组件!

最后一步是在 HTML 中使用 tuts-tab 。我们可以很容易地在 HTML 标记中插入 tuts-tab 。这是一个简单的例子来演示其用法。

<tuts-tabs background>
  <button slot="title">Tab 1</button>
  <button slot="title" selected>Tab 2</button>
  <button slot="title">Tab 3</button>
  <section>content panel 1</section>
  <section>content panel 2</section>
  <section>content panel 3</section>
</tuts-tabs>

结论

我们开始了!我们已经完成了一个重要教程,在该教程中我们创建并使用了自定义元素。该过程很简单,并且在开发网页时被证明非常有用。我希望您尝试创建自定义元素并与我们分享您的经验。

HTML速学教程(入门课程)
HTML速学教程(入门课程)

HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
typedef和define区别
typedef和define区别

typedef和define区别在类型检查、作用范围、可读性、错误处理和内存占用等。本专题为大家提供typedef和define相关的文章、下载、课程内容,供大家免费下载体验。

119

2023.09.26

define的用法
define的用法

define用法:1、定义常量;2、定义函数宏:3、定义条件编译;4、定义多行宏。更多关于define的用法的内容,大家可以阅读本专题下的文章。

387

2023.10.11

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

549

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

30

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

44

2026.01.06

DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

4329

2024.08.14

DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

4329

2024.08.14

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

9

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

22

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号