• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用自定义标签和 Shadow DOM 扩展 HTML

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

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

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

为什么要使用 Shadow DOM?

Shadow DOM 提供以下解决方案:

  • 允许 DOM 中的孤立元素。查询不会返回孤立的元素,例如document.queryselector.

  • 允许范围内的 CSS。Scoped CSS 确保所有样式规则都保留在页面内。这也意味着更简单的 CSS 选择器,没有任何命名冲突和许多泛型类。

我们的例子

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

See the Pen  Shadow DOM by Envato Tuts+ (@tutsplus)  on CodePen.

了解影子 DOM

什么是影子 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 元素。 

使用自定义标签和 Shadow DOM 扩展 HTML  第1张

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

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

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

接下来,我们将使用.innerHTML. 请注意,这不是填充 Shadow DOM 的唯一方法。有许多 api 可以帮助您填充 Shadow DOM。 

1    shadowRoot.innerHTML = ``

步骤 3. 将自定义元素连接到 Shadow DOM

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

在我们的示例中,我们使用customElements.define(). 如上一教程所述,新元素的名称中应该有一个“-”。并且,tuts-tabs 组件扩展了HTMLElement. 

当我们扩展HTMLElement时,在构造函数内部调用是很重要super()的。此外,构造函数是需要创建 shadowRoot 的地方。 



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>标签中,并且这些样式的范围仅限于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 添加样式

相关的 CSStuts-tab可以写在<style></style>标签里面。请记住,此处声明的所有样式都将作用于tuts-tabWeb 组件。Scoped CSS是 Shadow DOM 的一个有用特性。并且,它具有以下属性:

  • CSS 选择器不会影响 shadow DOM 之外的组件。

  • shadow DOM 中的元素不受其外部选择器的影响。

  • 样式的范围是宿主元素。

如果要在 shadow DOM 中选择自定义元素,可以使用:host伪类。当:host伪类用于普通的 DOM 结构时,它不会有任何影响。但是,在 shadow DOM 中,它产生了很大的不同。您将在组件中找到以下:host样式。tuts-tab它决定显示和字体样式。这只是一个简单的示例,展示了如何将其合并:host到影子 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>

同样,您可以自由地在影子 DOM 中引入样式表。当您在影子 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;

}

...

</style>

<link rel="stylesheet" href="styles.css">

...

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

接下来,我们可以定义tuts-tab.  

在一个简单的选项卡结构中,应该有可以点击的标题和反映所选标题内容的面板。这显然意味着,我们的自定义元素应该有一个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>. 我们的下一步是了解更多关于插槽的信息。

步骤 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 使用其中两种方法来构建选项卡:connectedcallback和disconnectedCallback.

connectedCallback我们将填充第 6 步中定义的插槽。我们的定义connectedCallback如下。我们利用querySelector来识别tabsSlotand panelsSlot。当然,这不是识别 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. 使用新组件!

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


<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>


See the Pen  Shadow DOM by Envato Tuts+ (@tutsplus)  on CodePen.

结论

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


文章目录
  • 为什么要使用 Shadow DOM?
  • 我们的例子
  • 了解影子 DOM
      • 什么是影子 DOM?
  • 使用 Shadow DOM 的分步指南
    • 步骤 1. 创建 Shadow DOM 元素
    • 步骤 2. 将内容添加到 Shadow Root
    • 步骤 3. 将自定义元素连接到 Shadow DOM
    • 步骤 4. 为 Shadow DOM 添加样式
    • 步骤 5. 在自定义组件中定义 HTML 元素
    • 步骤 6. 在 Shadow DOM 中使用插槽
    • 步骤 7. 填充插槽
    • 步骤 8. 实现逻辑和交互性
    • 步骤 9. 定义生命周期方法
    • 步骤 10. 使用新组件!
  • 结论
  • 发表评论