// Getting all matches up the tree #
// In jQuery, .parents() climbs the DOM tree and returns all parent elements. If you include a selector, it will only return those that match. Here’s the vanilla JavaScript equivalent:
/**
* Get all of an element's parent elements up the DOM tree
* @param {Node} elem The element
* @param {String} selector Selector to match against [optional]
* @return {Array} The parent elements
*/
var getParents = 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;
};
}
// Setup parents array
var parents = [];
// Get matching parent elements
for ( ; elem && elem !== document; elem = elem.parentNode ) {
// Add matching parents to array
if ( selector ) {
if ( elem.matches( selector ) ) {
parents.push( elem );
}
} else {
parents.push( elem );
}
}
return parents;
};
// And to use it:
var elem = document.querySelector('#some-element');
var parents = getParents(elem, '.some-class');
var allParents = getParents(elem.parentNode);
// Getting all matches up the tree until a matching parent is found #
// In jQuery, .parentsUntil() climbs the DOM tree and returns all parent elements until a matching parent is found. If you include a selector, it will only return those that match. Here’s the vanilla JavaScript equivalent:
/**
* Get all of an element's parent elements up the DOM tree until a matching parent is found
* @param {Node} elem The element
* @param {String} parent The selector for the parent to stop at
* @param {String} selector The selector to filter against [optionals]
* @return {Array} The parent elements
*/
var getParentsUntil = function ( elem, parent, 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;
};
}
// Setup parents array
var parents = [];
// Get matching parent elements
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( parent ) {
if ( elem.matches( parent ) ) break;
}
if ( selector ) {
if ( elem.matches( selector ) ) {
parents.push( elem );
}
break;
}
parents.push( elem );
}
return parents;
};
And to use it:
var elem = document.querySelector('#some-element');
var parentsUntil = getParentsUntil(elem, '.some-class');
var parentsUntilByFilter = getParentsUntil(elem, '.some-class', '[data-something]');
var allParentsUntil = getParentsUntil(elem);
var allParentsExcludingElem = getParentsUntil(elem.parentNode);
/**
* Get the closest matching element up the DOM tree.
* @private
* @param {Element} elem Starting element
* @param {String} selector Selector to match against
* @return {Boolean|Element} Returns null if not match found
*/
var getClosest = 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;
};
}
// Get closest match
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( elem.matches( selector ) ) return elem;
}
return null;
};
// Usage:
var elem = document.querySelector('#example');
var closestElem = getClosest(elem, '.sample-class');
// If you wanted to start with the element’s parent instead of
// the element itself (equivalent to the .parent() method), you would do this:
var elem = document.querySelector('#example');
var closestElem = getClosest(elem.parentNode, '#sample-id');