Skip to main content Accessibility Feedback

Climbing up and down the DOM tree with vanilla JavaScript

On a recent Javascript project, I needed to climb up the DOM tree looking for the first element that had a particular class, ID, or data attribute.

This is really easy to do with jQuery, but today I wanted to share some simple vanilla JavaScript methods to duplicate jQuery’s .closest(), .parent/s(), and .find() APIs.

Note: Updated on December 16, 2014, to include tag selectors.

Climbing up the DOM tree #

jQuery offers a few methods for climbing up the DOM tree. Let’s explore a few of the tasks you might want to achieve.

Getting the first match up the tree #

In jQuery, .closest() climbs up the DOM tree and finds the first element that has the selector you’re trying to match against. It starts with the first element, and then climbs. Here’s a vanilla JS method that does the same thing:

/**
 * 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;

};

And to use it:

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');

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

Once again, if you wanted to start with the element’s parent instead of the element itself, you would do this:

var elem = document.querySelector('#example');
var parents = getClosest(elem.parentNode, '[data-sample]');

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

Climbing down the DOM tree #

In jQuery, the .find() method provides an easy way to match elements down the DOM tree by selector. In vanilla JS, climbing down the DOM tree is actually a lot easier than going up, and can be achieved with the querySelector family of web API’s.

Getting the first match down the tree #

var elem = document.querySelector('#example');
var firstElem = elem.querySelector('.sample-class');

Getting all matches down the tree #

var elem = document.querySelector('#example');
var allElems = elem.querySelectorAll('[data-sample]');

Enjoy!


🚀 I just relaunched my Vanilla JS Pocket Guides with new code examples and real projects to help tie everything you’ll learn together. Check it out.

Have any questions or comments about this post? Email me at chris@gomakethings.com or contact me on Twitter at @ChrisFerdinandi.

Get Daily Developer Tips