代码之家  ›  专栏  ›  技术社区  ›  Danny '365CSI' Engelman

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

  •  1
  • Danny '365CSI' Engelman  · 技术社区  · 7 年前

    一 赶上 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> 在内部 <element-z> 阴影区

    发现 <元素x> 从内部后裔 <element z>

    必修的:

    A(递归) .closest() 向上移动(阴影)DOM的函数 s 发现 <

    注意:元素可能有也可能没有阴影(请参见 <element y>

    资源:

    使现代化

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

    更新#2

      closestElement(selector, el = this) {
        return (
          (el && el != document && el != window && el.closest(selector)) ||
          this.closestElement(selector, el.getRootNode().host)
        );
      }
    
    1 回复  |  直到 5 年前
        1
  •  6
  •   Danny '365CSI' Engelman    7 年前

    这与 .closest() 从任何子(阴影)DOM内部

    十字路口 阴影根边界

    优化(极端)缩小

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

    注意:最近的函数被声明为“参数”,以避免额外的 let

    从自定义元素内部调用:

    <element-x>
    //# shadow-root
        <element-y>
            <element-z>
            //# shadow-root
            let container = this.closestElement('element-x');
            </element-z>
        </element-y>
    </element-x>
    
        2
  •  4
  •   Louis St-Amour    7 年前

    很好的例子!想要贡献一个有细微区别的TypeScript版本——它在沿着阴影根遍历时跟随assignedSlot,这样您就可以在嵌套的、开槽的自定义元素链中找到最匹配的元素。这不是写打字稿的最奇特的方式,但它完成了工作。

    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);
    }
    
        3
  •  0
  •   Mendy    5 年前

    像这样的东西应该能奏效

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