Skip to content Skip to sidebar Skip to footer

Custom Element Getrootnode.closest() Function Crossing Multiple (parent) Shadowdom Boundaries

I spent some time searching but have only seen too many regular 'walk the DOM' blogs or answers that only go one level UP with getRootnode() Pseudo code: HTML //#

Solution 1:

This does the same as .closest() from inside any child (shadow)DOM

but walking up the DOM crossing shadowroot Boundaries

Optimized for (extreme) minification

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

Note: the __Closest function is declared as 'parameter' to avoid an extra let declaration... better for minification, and keeps your IDE from complaining

Called from inside a Custom Element:

<element-x>
//# shadow-root
    <element-y>
        <element-z>
        //# shadow-rootlet container = this.closestElement('element-x');
        </element-z>
    </element-y>
</element-x>

Solution 2:

Excellent examples! Wanted to contribute a TypeScript version that has a minor difference -- it follows assignedSlot while traversing up the shadow roots, so you can find the closest matching element in a chain of nested, slotted custom elements. It's not the fanciest way to write the TypeScript, but it gets the job done.

closestElement(selector: string, base: Element = this) {
  function__closestFrom(el: Element | Window | Document): Element {
    if (!el || el === document || el === window) returnnull;
    if ((el asSlotable).assignedSlot) el = (el asSlotable).assignedSlot;
    let found = (el asElement).closest(selector);
    return found
      ? found
      : __closestFrom(((el asElement).getRootNode() asShadowRoot).host);
  }
  return__closestFrom(base);
}

The equvalent in JS is:

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

Solution 3:

Something like this should do the trick

functionclosestPassShadow(node, selector) {

    if (!node) {
        returnnull;
    }

    if (node instanceofShadowRoot) {
        returnthis.closestPassShadow(node.host, selector);
    }

    if (node instanceofHTMLElement) {
        if (node.matches(selector)) {
            return node;
        } else {
            returnthis.closestPassShadow(node.parentNode, selector);
        }
    }

    returnthis.closestPassShadow(node.parentNode, selector);

}

Post a Comment for "Custom Element Getrootnode.closest() Function Crossing Multiple (parent) Shadowdom Boundaries"