Finding the Closest Element Through Shadow Roots
•
1 min read
A great trick to find the closest element in the DOM that matches an arbitrary selector is Element.closest()
.
// Starts at el and walks the DOM until it finds a parent element
// that matches the selector. In this case, it will return the
// <body> element.
el.closest('body');
But what happens if you do this from inside a custom element's shadow root?
someElementInShadowRoot.closest('body');
By design, Element.closest()
will not break out of the shadow root, so null
is returned.
In my case, I needed to determine the lang
of the closest element, even if the element was outside of a shadow root. Time for some recursive magic! ✨
Here's a TypeScript function that will do just that, even if the root is buried in multiple layers of shadow roots.
function closest(selector: string, root: Element = this) {
function getNext(el: Element | HTMLElement, next = el && el.closest(selector)): Element | null {
if (el instanceof Window || el instanceof Document || !el) {
return null;
}
return next ? next : getNext((el.getRootNode() as ShadowRoot).host);
}
return getNext(root);
}
You can use it like this:
// Matches the closest element with a lang attribute, even if
// it's outside of the shadow root
const closestEl = closest('[lang]', el);