自定义元素 getRootNode.closest() 函数跨越多个(父)shadowDOM 边界

Dan*_*man 7 javascript shadow-dom custom-element native-web-component

我花了一些时间进行搜索,但只看到太多常规的“遍历 DOM”博客或仅提升一个级别的答案getRootnode()

伪代码:

HTML

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-root
        let container = this.closest('element-x');
        </element-z>
    </element-y>
</element-x>
Run Code Online (Sandbox Code Playgroud)

标准element.closest()功能并没有捅破阴影边界;

所以this.closest('element-x')返回null因为没有 <element-x><element-z>shadowDom

目标:

<element-x>从后代内部查找<element z>(任何嵌套级别)

必需的:

A(递归).closest()函数,走了(阴影)DOM小号和认定<element-x>

注意:元素可能有也可能没有 ShadowDOM(参见<element y>:仅 lightDOM)

我可以而且明天会自己做;只是想知道是否有聪明的头脑已经做到了。

资源:

更新

这是以下答案中的未缩小代码:

        closestElement(selector, base = this) {
            function __closestFrom(el) {
                if (!el || el === document || el === window) return null;
                let found = el.closest(selector);
                if (found)
                  return found;
                else
                  __closestFrom(el.getRootNode().host);
            }

            return __closestFrom(base);
        }
Run Code Online (Sandbox Code Playgroud)

更新 #2

我将其更改为 BaseElement 上的一个方法:

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-root
        let container = this.closest('element-x');
        </element-z>
    </element-y>
</element-x>
Run Code Online (Sandbox Code Playgroud)

Men*_*ndy 8

像这样的事情应该可以解决问题

function closestPassShadow(node, selector) {

    if (!node) {
        return null;
    }

    if (node instanceof ShadowRoot) {
        return this.closestPassShadow(node.host, selector);
    }

    if (node instanceof HTMLElement) {
        if (node.matches(selector)) {
            return node;
        } else {
            return this.closestPassShadow(node.parentNode, selector);
        }
    }

    return this.closestPassShadow(node.parentNode, selector);

}
Run Code Online (Sandbox Code Playgroud)


Lou*_*our 7

优秀的例子!我想贡献一个有细微差别的 TypeScript 版本——它在遍历影子根时遵循分配的槽位,这样你就可以在嵌套的、开槽的自定义元素链中找到最接近的匹配元素。这不是编写 TypeScript 的最奇特的方式,但它可以完成工作。

\n

编辑:这段代码是 5 年前编写的,从那时起,typescript 本身已经取得了长足的进步。许多过去需要特殊处理的 JS 代码现在应该可以在 TypeScript 中使用 \xe2\x80\x9c 来工作\xe2\x80\x9d。这段代码可以而且很可能应该被简化。当然可以使用它,但我今天\xe2\x80\x99m 并不像 5 年前那样为它感到自豪:) 然而,我没有时间编辑它。

\n
closestElement(selector: string, base: Element = this) {\n  function __closestFrom(el: Element | Window | Document): Element {\n    if (!el || el === document || el === window) return null;\n    if ((el as Slotable).assignedSlot) el = (el as Slotable).assignedSlot;\n    let found = (el as Element).closest(selector);\n    return found\n      ? found\n      : __closestFrom(((el as Element).getRootNode() as ShadowRoot).host);\n  }\n  return __closestFrom(base);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

JS 中的等价物是:

\n
closestElement(selector, base = this) {\n    function __closestFrom(el) {\n        if (!el || el === document || el === window)\n            return null;\n        if (el.assignedSlot)\n            el = el.assignedSlot;\n        let found = el.closest(selector);\n        return found\n            ? found\n            : __closestFrom(el.getRootNode().host);\n    }\n    return __closestFrom(base);\n}\n
Run Code Online (Sandbox Code Playgroud)\n


小智 5

这与任何子(阴影)DOM 内部的.closest()相同

但是沿着 DOM穿越shadowroot 边界

针对(极端)缩小进行了优化

//declared as method on a Custom Element:
closestElement(
    selector,      // selector like in .closest()
    base = this,   // extra functionality to skip a parent
    __Closest = (el, found = el && el.closest(selector)) => 
        !el || el === document || el === window
            ? null // standard .closest() returns null for non-found selectors also
            : found 
                ? found // found a selector INside this element
                : __Closest(el.getRootNode().host) // recursion!! break out to parent DOM
) {
    return __Closest(base);
}
Run Code Online (Sandbox Code Playgroud)

注意:__Closest 函数被声明为“参数”以避免额外的let声明......更好地缩小,并防止您的 IDE 抱怨

从自定义元素内部调用:

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-root
        let container = this.closestElement('element-x');
        </element-z>
    </element-y>
</element-x>
Run Code Online (Sandbox Code Playgroud)