Skip to main content Accessibility Feedback

How to get all sibling elements until a match is found with vanilla JavaScript

One of the students in my Vanilla JS Slack channel (a private channel included with my pocket guides) asked me how to replicate jQuery’s nextUntil() method in vanilla JavaScript.

nextUntil() gets all sibling elements following an element until you reach an element with a particular selector. You can optionally filter sibling elements by a selector as well (for example, only returning elements with a certain class or data attribute).

Today, let’s build a vanilla JS version of this.

Setting up our function #

First, let’s create a function named nextUntil().

var nextUntil = function () {
    // Code goes here...
};

Next, let’s pass in a few arguments.

We need to know the element to start our search with. We also need to know what selector to check for and stop at (the “until” part of the script).

var nextUntil = function (elem, selector) {
    // Code goes here...
};

Now we’re ready to start coding.

Getting sibling elements #

First, let’s setup an array to push our sibling elements into.

var nextUntil = function (elem, selector) {

    // Setup siblings array
    var siblings = [];

};

Then, we’ll get the first sibling for our starting element. To do this, we’ll use the nextElementSibling property. There’s also a nextSibling property, but this returns all types of nodes (text strings, for example), and we only want elements.

var nextUntil = function (elem, selector) {

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

};

Now that we’ve got our first sibling, we want to do a few things:

  1. Make sure it doesn’t match our selector.
  2. Add it to the list of siblings.
  3. Get the next sibling element.

We’ll wrap all of this functionality in a while loop.

while loops run as long as the condition you’ve specified is true. In our case, we’ll reset the elem variable to the next sibling, and as long as an elem exists, we’ll continue looping through.

We’ll use matches() to check if the element has our selector or not, and push() to add the current element in the loop to our array.

var nextUntil = function (elem, selector) {

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

    // As long as a sibling exists
    while (elem) {

        // If we've reached our match, bail
        if (elem.matches(selector)) break;

        // Otherwise, push it to the siblings array
        siblings.push(elem);

        // Get the next sibling element
        elem = elem.nextElementSibling;

    }

};

Once our loop is done, we can return the siblings array.

var nextUntil = function (elem, selector) {

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

    // As long as a sibling exists
    while (elem) {

        // If we've reached our match, bail
        if (elem.matches(selector)) break;

        // Otherwise, push it to the siblings array
        siblings.push(elem);

        // Get the next sibling element
        elem = elem.nextElementSibling;

    }

    return siblings;

};

Looking good so far!

Optional filter #

The jQuery version of nextUntil() let’s you filter your siblings by a selector. I’d like to support that, too.

First, we’ll add a new argument to our function.

var nextUntil = function (elem, selector, filter) {
    // ...
};

Then, in our loop, after we check to see if the element matches our selector, we’ll check to see if a filter was specified. If it was, we’ll then check to see if the element matches the filter.

If it doesn’t, we’ll skip to the next sibling element. Otherwise, we’ll carry on as normal.

var nextUntil = function (elem, selector, filter) {

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

    // As long as a sibling exists
    while (elem) {

        // If we've reached our match, bail
        if (elem.matches(selector)) break;

        // If filtering by a selector, check if the sibling matches
        if (filter && !elem.matches(filter)) {
            elem = elem.nextElementSibling;
            continue;
        }

        // Otherwise, push it to the siblings array
        siblings.push(elem);

        // Get the next sibling element
        elem = elem.nextElementSibling;

    }

    return siblings;

};

Polyfilling matches() #

The matches() method was implemented with a browser prefix in some older browsers. A simple polyfill normalizes behavior in older versions of webkit and MS browsers.

Let’s add one to our script to ensure maximum cross-browser compatibility. This brings us to IE9+.

var nextUntil = function (elem, selector, filter) {

    // matches() polyfill
    if (!Element.prototype.matches) {
        Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
    }

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

    // As long as a sibling exists
    while (elem) {

        // If we've reached our match, bail
        if (elem.matches(selector)) break;

        // If filtering by a selector, check if the sibling matches
        if (filter && !elem.matches(filter)) {
            elem = elem.nextElementSibling;
            continue;
        }

        // Otherwise, push it to the siblings array
        siblings.push(elem);

        // Get the next sibling element
        elem = elem.nextElementSibling;

    }

    return siblings;

};

And that’s it! You can also download this script on GitHub.


🚀 Make 2018 the year you master JavaScript! My pocket guides and mini courses are short, focused, and made for beginners. You can do this!

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