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

时间:2019-02-04 16:38:58

标签: 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>

标准element.closest()函数不会穿透阴影边界;

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

目标:

从后代<element-x>(任何嵌套级别)中找到<element z>

必填:

一个(递归).closest()函数,用于遍历(阴影)DOM s 并找到<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);
                return found ? found : __closestFrom(el.getRootNode().host);
            }

            return __closestFrom(base);
        }

3 个答案:

答案 0 :(得分:2)

这与任何子(阴影)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);
}

注意:__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>

答案 1 :(得分:0)

优秀的例子!希望提供一个具有细微差别的TypeScript版本-在遍历阴影根时遵循AssignSlot,因此您可以在嵌套的,带槽的自定义元素链中找到最接近的匹配元素。这不是编写TypeScript的最理想的方法,但是可以完成工作。

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

JS中的等效项是:

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

答案 2 :(得分:0)

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

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

}