Skip to main content Accessibility Feedback

How to get all siblings of an element until a selector is found with vanilla JS

This week, we’ve looked at how to get the next and previous siblings of an element, how to get all siblings of an element, and how to find the next sibling that matches a selector.

But what if you wanted to get all siblings before or after an element until you found one with a specific selector?

Today, we’ll look at how to do that with vanilla JS.

An example

Let’s say you had a list of items.

<ul>
	<li>Item 1</li>
	<li class="stop-here">Item 2</li>
	<li>Item 3</li>
	<li>Item 4</li>
	<li id="find-me">Item 5</li>
	<li>Item 6</li>
	<li>Item 7</li>
	<li class="stop-here">Item 8</li>
	<li>Item 9</li>
</ul>

You have the #find-me element, and you want to get all the siblings after it until you reach the .stop-here list item.

Creating a helper function.

We’re going to create a helper function that will loop through each nextElementSibling, pushing them to an array, until it finds one that matches our selector.

First, we’ll setup an empty array to push the siblings to, and grab the first nextElementSibling.

var getNextUntil = function (elem, selector) {

	// Setup siblings array and get next sibling
	var siblings = [];
	var next = elem.nextElementSibling;

};

Then, we’ll use a while loop to iterative over each nextElementSibling until we find a match. In the loop, we’ll check if the current next sibling matches our selector using the matches() method.

If it does match, we’ll break from the loop. Otherwise, we’ll push the item to the siblings array and set next to the next sibling.

When the loop ends, we’ll return the siblings.

var getNextUntil = function (elem, selector) {

	// Setup siblings array and get next sibling
	var siblings = [];
	var next = elem.nextElementSibling;

	// Loop through all siblings
	while (next) {

		// If the matching item is found, quit
		if (next.matches(selector)) break;

		// Otherwise, push to array
		siblings.push(next);

		// Get the next sibling
		next = next.nextElementSibling

	}

	return siblings;

};

There’s one small tweak we can make to make this even better.

Imagine you wanted to get all siblings after an element, and didn’t care about checking for a selector. In our if statment, we’ll first make sure a selector was provided. If not, we’ll get back a list of all siblings after an element.

var getNextUntil = function (elem, selector) {

	// Setup siblings array and get next sibling
	var siblings = [];
	var next = elem.nextElementSibling;

	// Loop through all siblings
	while (next) {

		// If the matching item is found, quit
		if (selector && next.matches(selector)) break;

		// Otherwise, push to array
		siblings.push(next);

		// Get the next sibling
		next = next.nextElementSibling

	}

	return siblings;

};

To go in the other direction and get all previous siblings until a selector is found, we’ll use more or less the same code. We’ll just change nextElementSibling to previousElementSibling.

var getPreviousUntil = function (elem, selector) {

	// Setup siblings array and get previous sibling
	var siblings = [];
	var prev = elem.previousElementSibling;

	// Loop through all siblings
	while (prev) {

		// If the matching item is found, quit
		if (selector && prev.matches(selector)) break;

		// Otherwise, push to array
		siblings.push(prev);

		// Get the previous sibling
		prev = prev.previousElementSibling

	}

	return siblings;

};

How to use the helpers

Here are some samples of how you would use these helper functions.

var findMe = document.querySelector('#find-me');

// Returns the first two list items after #find-me
var nextStop = getNextUntil(findMe, '.stop-here');

// Returns all list items after #find-me
var nextAll = getNextUntil(findMe);

// Returns the first two list items before #find-me
var prevStop = getPreviousUntil(findMe, '.stop-here');

// Returns all list items before #find-me
var prevAll = getPreviousUntil(findMe);

Here’s a working demo for you to play with.

Browser compatibility

These helper methods work in all modern browsers, and back to IE9.

But… the matches() method was implemented inconsistently with vendor prefixes across many browsers for a while. You should include a polyfill for it to make sure these work properly.

You can download both helper methods at the Vanilla JS Toolkit, too.