在有插槽问题的 Web 组件中使用事件委托(冒泡)

Posted

技术标签:

【中文标题】在有插槽问题的 Web 组件中使用事件委托(冒泡)【英文标题】:Using event delegation (bubbling) in web component with slot problem 【发布时间】:2022-01-16 19:13:12 【问题描述】:

我创建了这个简单的示例(纯 JS,不在 Vue 或 React 等中):

<body>
  <script>
    (function () 
      class MyComponent extends htmlElement 
        constructor() 
          super();
          const shadowRoot = this.attachShadow( mode: 'open' );
          const template = document.createElement('template');
          template.innerHTML = `
            <div>
              <button>
                <slot> <em>left</em> </slot>
              </button>
            </div>
          `;
          shadowRoot.append(template.content.cloneNode(true))

          shadowRoot.addEventListener('click', (event) => 
            // normal event delegation stuff,
            const button = event.target.closest('button');
            if (!button) return;
            // do somthing with button
            console.log(button);
          );
        
      

      customElements.define('my-component', MyComponent);
    ());
  </script>

  <my-component id="myComponent"></my-component>
</body>

目前运行良好。

但是,在添加槽之后:

<my-component id="myComponent"> <span>previous</span> </my-component>

事件委托代码坏了,因为我点击的是Light DOM但Shadow DOM,所以,我用const button = event.target.closest('button');得到了null

有什么建议在槽内使用事件委托?

如果语法有问题,我是中国人:) 感谢您的阅读

【问题讨论】:

请创建一个 MCVE。 ***.com/help/mcve。另请注意,使用插槽时,插槽内容保留在 light DOM 中;它只是为了渲染目的反映到shadow DOM。这不会改变它在文档中的位置,因此 light DOM 中的任何事件都不会看到 shadow DOM。 @connexo 谢谢,添加了 MCVE。您的评论帮助我确认了我的想法,我认为我必须以另一种方式实现相同的目标。 【参考方案1】:

event.composedPath() 输出包含 click 事件传递的所有元素。 跨越所有 shadowRoot 边界。

我把你的代码压缩了一个...

注意:SO sn-p 控制台速度慢且冗长;检查 TOP OF F12 控制台(所以 sn-p 也可以在那里列出沙盒错误)

<script>
  customElements.define('my-component', 
   class extends HTMLElement 
    constructor() 
      super()
       .attachShadow(mode:'open')
       .innerHTML = `<div>
                       <button><slot>Left</slot></button>
                     </div>`;
      this.shadowRoot.addEventListener('click', (evt) => 
        console.log(evt.composedPath());
      );
    
  );
</script>

<my-component></my-component>
<my-component>Right</my-component>

【讨论】:

非常感谢,evt.composedPath() 有效。我只需要在composedPath 数组中找到正确的目标。

以上是关于在有插槽问题的 Web 组件中使用事件委托(冒泡)的主要内容,如果未能解决你的问题,请参考以下文章

web前端入门到实战:JavaScript 事件冒泡与事件委托

延迟加载组件

DOM事件机制(事件捕获和事件冒泡和事件委托)

JS中的事件(对象,冒泡,委托,绑定)

深入理解-事件委托

JavaScript中冒泡与事件委托