Skip to main content Accessibility Feedback

Recursion with vanilla JavaScript

In JavaScript, recursion is when you call a function from within itself until or unless a condition is a met. Today, let’s look at how it works.

An example

Let’s say you have a collection of nested elements, like this:

<div class="bg-1">
	<div class="bg-2">
		<div class="bg-3">
			<div class="bg-4">
				<div class="bg-5">
					<div class="bg-6">
						<div class="bg-7">
							...
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</div>

You want to write a JavaScript function that let’s you get the distance in the DOM tree between an element and it’s parent. For example, .bg-5 is two levels above .bg-7.

Creating a helper function

First, let’s create a levelsUp() helper function.

We’ll pass in an elem and selector as arguments. We’ll also add a third argument that we’ll use to track the distance between our elem and an element with the selector.

var levelsUp = function (elem, selector, distance) {
	// Do stuff...
};

We don’t want users to have to set distance to 0 every time they run the function.

We’ll check to see if a value for distance exists, and if not, we’ll set it to 0. Then, we’ll use ++ to increment our distance value by 1.

var levelsUp = function (elem, selector, distance) {

	// If distance isn't defined yet, set it to 0
	if (!distance) {
		distance = 0;
	}

	// Increase the distance by 1
	distance++;

};

Next, we’ll get the parent of the current elem with the elem.parentNode property.

var levelsUp = function (elem, selector, distance) {

	// If distance isn't defined yet, set it to 0
	if (!distance) {
		distance = 0;
	}

	// Increase the distance by 1
	distance++;

	// Get the parent of the current element
	var parent = elem.parentNode;

};

Now, we can use the matches() method to check if the parent matches the selector.

If the parent is a match, we can return our distance.

var levelsUp = function (elem, selector, distance) {

	// If distance isn't defined yet, set it to 0
	if (!distance) {
		distance = 0;
	}

	// Increase the distance by 1
	distance++;

	// Get the parent of the current element
	var parent = elem.parentNode;

	// If we've reached the parent, return the distance
	if (parent.matches(selector)) return distance;

};

Adding recursion

Now, here’s where the recursion comes in.

If the parent isn’t a match, we want to run levelsUp() again, using the parent as our starting element. We’ll also pass in the selector, and our current distance.

And because the function ultimately needs to return a value, we’ll return whatever the output of our recursive levelsUp() function is.

var levelsUp = function (elem, selector, distance) {

	// If distance isn't defined yet, set it to 0
	if (!distance) {
		distance = 0;
	}

	// Increase the distance by 1
	distance++;

	// Get the parent of the current element
	var parent = elem.parentNode;

	// If we've reached the parent, return the distance
	if (parent.matches(selector)) return distance;

	// Otherwise, recursively run levelsUp() again
	return levelsUp(parent, selector, distance);

};

The levelsUp() method will run multiple times until it finds a match or hits the window element, and will return whatever the final value for distance is (or -1 if no match is found).

And with that, we now have a recursive function.

One last detail

If there’s no matching selector, you might end up far enough up the DOM tree that you hit an element that does not support the matches() method (like the window).

Before trying to use matches(), let’s first check if the parent supports that method. If not, we’ll return -1 and assume there’s no match.

var levelsUp = function (elem, selector, distance) {

	// If distance isn't defined yet, set it to 0
	if (!distance) {
		distance = 0;
	}

	// Increase the distance by 1
	distance++;

	// Get the parent of the current element
	var parent = elem.parentNode;

	// If you we reach an element with no matches() method, bail
	if (!parent.matches) return -1;

	// If we've reached the parent, return the distance
	if (parent.matches(selector)) return distance;

	// Otherwise, recursively run levelsUp() again
	return levelsUp(parent, selector, distance);

};

Try it yourself

I’ve put together a demo on CodePen for you.

Play around, try it out, and let me know if you have any questions.