首页 > 解决方案 > 有没有办法防止来自 light dom 中的开槽元素的事件通过 shadow dom 传播?

问题描述

问题

我有一个来自第三方库的复杂元素 P,它监听用户交互触发的事件。我想编写一个在其 shadow dom 中包含 P 的 Web 组件,并使用插槽机制,我希望将任何元素 C 元素放入 W 的 light dom 中,以显示在 P 中的某个位置。

我的问题如下:对于交互式元素 C,我希望事件直接传播到 light dom,而不触发 P 中的任何最终事件侦听器。

我试过的

我没有直接在 Pherachy 中添加插槽,而是尝试在我创建的另一个元素中添加插槽,在 Pherachy 中添加此元素并在此元素中冒泡时停止事件传播。在封装方面,这个元素不是来自 light dom 的 sloted 元素的父元素,但这样做仍然会阻止事件到达 W。

举例汇报情况

创建 P (P.js) 的外部库:

export function P(container) {
    const superComplexInnerHierachy = document.createElement("div")

    superComplexInnerHierachy.textContent = "Some P lib's interactive stuff"

    superComplexInnerHierachy.addEventListener(
        "click",
        () => console.log("I'm the third party P lib, I do stuff on click.")
    )

    container.append(d1)

    const thingsIDo = {
      add : (elem) => {
        superComplexInnerHierachy.append(elem)
        superComplexInnerHierachy.append("More P lib's interactive stuff")
      }
    }

    return thingsIDo

}

我正在尝试编写的网络组件 W(W.js):

import {P} from "P.js"

class W extends HTMLElement {
    constructor(){

        this.attachShadow({mode : "open"})

        this.value = "Something else"

        // Create the lib stuff in the shadow root
        this.p = P(this.shadowRoot)

        // Add a slot in P's hierachy to inject an element from the light dom
        const slot = document.createElement("slot")
        this.p.add(slot)
    }
}
customElements.define("w-component", W);

使用 W 的 html 片段。

<script type="module" src="W.js"></script>

<w-component>
   <div name="an_interactive_element_usupecting_of_P">
      <input type="button" value="Button A">
      <input type="button" value="Button B">
   </div>

</w-component>

<script type="text/javascript">
    document.querySelector("w-component")
        .addEventListener("click", evt => {
            console.log(`${evt.target.value} was clicked`)
        })
</script>

行为

当前代码的行为

点击 A 时

I'm the third party P lib, I do stuff on click.

Button A was clicked

点击P添加的东西时

I'm the third party P lib, I do stuff on click.

Something else was clicked

想拥有什么

点击 A 时

Button A was clicked

点击P添加的东西时

I'm the third party P lib, I do stuff on click.

Something else was clicked

标签: ecmascript-6web-componentdom-eventsshadow-dom

解决方案


一种解决方案是在事件传输到 Shadow DOM 之前捕获addEventListener()事件,方法是将第三个参数设置为to true

如果您检测到它来自一个开槽元素并且您可以自己处理它,请调用stopPropagation()以防止其传播。

document.querySelector("w-component").addEventListener("click", evt => {
    console.log(`${evt.target.value} was clicked`)
    if ( evt.path.findIndex( elem => elem.localName === "slot" ) !== -1 )
        evt.stopPropagation()
}, true )

推荐阅读