Climbing up the DOM until you hit a match with vanilla JavaScript
Yesterday, we looked at how to get all parent elements with vanilla JavaScript. Today, we’re going to modify our script to stop when it finds an element with a specific selector.
Getting Setup
Here’s yesterday’s script, renamed getParentsUntil
.
var getParentsUntil = function (elem, selector) {
// Element.matches() polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Set up a parent array
var parents = [];
// Push each parent element to the array
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if (selector) {
if (elem.matches(selector)) {
parents.push(elem);
}
continue;
}
parents.push(elem);
}
// Return our parent array
return parents;
};
As we climb up the DOM, we’re pushing each parent element to an array. We also provide an option to only add elements that have a particular selector (for example, the class .pick-me
).
Stopping when you get to a specific element
To stop when we hit a certain element, we need to do two things:
- Add a third argument that let’s us specify a selector to stop at.
- Check if each parent element has that selector (and if so, quit our loop).
We’ll again use matches()
to check our new selector, which we’ll call stop
. If the current parent element matches the stop
selector, we’ll use break
to end our loop.
var getParentsUntil = function (elem, selector, stop) {
// Element.matches() polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Set up a parent array
var parents = [];
// Push each parent element to the array
for ( ; elem && elem !== document; elem = elem.parentNode ) {
// Should we stop at this parent element?
if (stop && elem.matches(stop)) break;
if (selector) {
if (elem.matches(selector)) {
parents.push(elem);
}
continue;
}
parents.push(elem);
}
// Return our parent array
return parents;
};
To use our new script, you’d do this.
var elem = document.querySelector('.some-element');
var parents = getParents(elem, '.pick-me', '.stop-here');
You can get the completed getParentsUntil()
helper function on GitHub.