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
<element-x>
//# shadow-root
<element-y>
<element-z>
//# shadow-root
let container = this.closest('element-x');
</element-z>
</element-y>
</element-x>
The standard element.closest()
function does not pierce shadow boundaries;
So this.closest('element-x')
returns null
because there is no <element-x>
within <element-z>
shadowDom
Goal:
Find <element-x>
from inside descendant <element z>
(any nested level)
Required:
A (recursive) .closest()
function that walks up the (shadow) DOMs and finds <element-x>
Note: elements may or may not have ShadowDOM (see <element y>
: only lightDOM)
I can and will do it myself tomorrow; just wondered if some bright mind had already done it.
Resources:
- https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
- https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/host
Update
This is the UNminified code from the answer below:
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);
}
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.
The equvalent in JS is:
This does the same as .closest() from inside any child (shadow)DOM
but walking up the DOM crossing shadowroot Boundaries
Optimized for (extreme) minification
Note: the __Closest function is declared as 'parameter' to avoid an extra
let
declaration... better for minification, and keeps your IDE from complainingCalled from inside a Custom Element: