Shadow_Dom

  1. 核心概念
  2. 主要特点
  3. 实际应用场景
  4. 特别注意

生产活动中遇到将多个html注入到一个index.html的需求,如果使用文本处理或者直接将页面内容分类注入的话,会产生很多诸如命名冲突、逻辑冲突、布局混乱等问题,这时候就需要将不同子网页内容隔离开来,也就需要用到Shadow Dom

Shadow DOM(影子 DOM)是 Web Components 技术栈中的关键特性之一,它允许将封装的、独立的 DOM 子树附加到常规 DOM 树中的元素上,从而实现样式和标记的封装。

核心概念

  1. DOM 封装
    Shadow DOM 内部的元素不会暴露给外部 DOM 的选择器(如 querySelector、CSS 选择器)。

样式隔离:外部样式不会影响 Shadow DOM 内部,内部样式也不会影响外部(默认情况下)。

  1. 创建方式
const element = document.createElement('div');
// 创建 shadow root(有两种模式)
const shadowRoot = element.attachShadow({ mode: 'open' }); // 可通过 element.shadowRoot 访问
// const shadowRoot = element.attachShadow({ mode: 'closed' }); // shadowRoot 为 null,不可外部访问

// 向 shadow DOM 添加内容
shadowRoot.innerHTML = `
  <style>
    p { color: blue; }
  </style>
  <p>这段文字在 Shadow DOM 内部</p>
`;

主要特点

  1. 样式封装
    外部 CSS 不会影响 Shadow DOM 内部(除了一些继承属性如字体、颜色等)。

内部 CSS 不会影响外部元素。

可使用 :host 伪类选择器定义宿主元素的样式:

:host {
  display: block;
  border: 1px solid #ccc;
}
  1. 插槽(Slots)
    允许在自定义组件中插入外部内容。
<!-- 自定义组件模板 -->
<template id="my-card">
  <div class="card">
    <slot name="title">默认标题</slot>
    <slot>默认内容</slot>
  </div>
</template>

<!-- 使用 -->
<my-card>
  <h1 slot="title">自定义标题</h1>
  <p>这是卡片内容</p>
</my-card>
  1. 事件处理
    事件在 Shadow DOM 内部触发时,外部默认看到的是宿主元素的事件(可通过 event.composedPath() 查看事件路径)。

需要跨边界的事件需使用 composed: true:

new CustomEvent('my-event', { 
  bubbles: true, 
  composed: true 
});

实际应用场景

Web Components 开发(结合 Custom Elements 和 Templates)

封装第三方小部件(如视频播放器、复杂 UI 控件)

样式隔离的组件库

避免 CSS 类名冲突

示例:简单自定义组件

<user-card name="张三"></user-card>

<script>
class UserCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        :host { display: inline-block; padding: 10px; border: 1px solid #ddd; }
        .name { color: red; font-weight: bold; }
      </style>
      <div>用户名:<span class="name">${this.getAttribute('name')}</span></div>
    `;
  }
}
customElements.define('user-card', UserCard);
</script>

特别注意

由于隔离性,要注入的html的script部分不能再用document去获取shadow dom自身的element,而使用shadow,这个shadow表示对于此部件自身的shadowDom

想要子html调用,需要在主脚本负责注入逻辑处将shadow暴露出来,如

    const shadowRoot = (component as any).__shadow;

    scripts.forEach(code => {
      // 直接执行,并把 shadowRoot 作为参数传进去
      const func = new Function('shadowRoot', code);
      func.call(shadowRoot, shadowRoot);
    });

然后子脚本就可以通过shadowRoot获取到shadow中的元素、样式标签等

Shadow DOM 是实现组件级封装的强大工具,特别适合构建可复用、隔离性强的 UI 组件。


此方悬停
相册 小说 Ai