Opposite of closest in javascript

I have a code with lots of submenus that share the same class name.

Here's a structure:

.menu
  .sub-menu
  .sub-menu
    .sub-menu
    .sub-menu
  .sub-menu
    .sub-menu
      .elem
      .elem
  .sub-menu

Note that .sub-menu may be infinite levels deep.

So how do I achieve this: when .elem is clicked, I want to travers the DOM upwards until the top-most .sub-menu is reached and apply a style to it. I am aware of .closest() and .parent() and .find(), but I have no idea if jQuery has such feature such as .topMost(selector)?

The only way I can think of is maybe running a loop and going through .closest('.sub-menu') of the new element until its length is zero (there are no more parents with this class, so it must be the top-most). However, I think there should be a more practical approach to this.

Here’s a boil-down on the jQuery’s .closest() method and 2 native JavaScript alternatives.

jQuery’s .closest() function is great! Here’s what it does:

For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.

Essentially, the closest function has two things:

  1. A starting point — start from here and traverse the DOM up.
  2. The element to look for — stop the traverse when hit this element and return it.

Here's our example markup that we'll be working with:

<div>
  <nav>
    <ul>
      <li class="nav-item-1">item1li>
      <li class="nav-item-2">item2li>
      <li class="nav-item-3">item3li>
      <li class="nav-item-4">item4li>
    ul>
  nav>
div>

Imagine you want to do something to the nav, but your 'starting point' is one of the li elements (for whatever reason). You might be tempted to do this:

$('.nav-item-1')
  .parent() // ul
  .parent() // nav
  .parent() // div
  .css('background-color', 'red')

That's not too bad, but what if you need to traverse longer distance? Or if you add a wrapper there you need to update the code. Closest is made just for these kinds of thing:

$('.nav-item-1').closest('nav').css('background-color', 'red')

You give the starting point $('.nav-item-1') and the ending point .closest('nav'), and it will traverse the DOM up until it hits the target and returns it.

Pure JavaScript closestByClass function

Below is vanilla JavaScript function that gets the closest by class name. From it, you can see much more in detail how the closest function really works:

var closestByClass = function (el, clazz) {
  // Traverse the DOM up with a while loop
  while (el.className != clazz) {
    // Increment the loop to the parent node
    el = el.parentNode
    if (!el) {
      return null
    }
  }

  // At this point, the while loop has stopped and `el` represents the element
  // that has the class you specified in the second parameter of the function
  // `clazz`
  return el
}

Example usage:

document.onclick = function (e) {
  if (closestByClass(e.target, 'some-class')) {
    // Do something
  } else {
    // Do something else
  }
}

That's nice and all, but it only takes class names, it's easy to make it check for ID, just replace the className with id. Where as it might be totally sufficient in majority of use cases, it's still not very versatile.

Recursive pure JavaScript closest

Here's a more flexible function that uses recursion and is a bit shorter:

var closest = function (el, fn) {
  return el && (fn(el) ? el : closest(el.parentNode, fn))
}

Or write it as one-liner with an arrow function:

var closest = (el, fn) => el && (fn(el) ? el : closest(el.parentNode, fn))

That's quite dense. Let's look at the usage with the example list we used earlier. Note how easy it is to target class, id, or tag name:

var srcEl = document.getElementsByClassName('nav__item-3')

// The element with a class of `.nav` is the wanted element in this case
var nav = closest(srcEl[0], function (el) {
  // Here's the beauty of this function, we have control on the target, here
  // we're using class name `.nav`
  return el.className === 'nav'
})

// Now the variable `nav` contains the closest element with a class `.nav`
console.log(nav)

// Here the target is given as id `#nav-id`
var nav = closest(srcEl[0], el => el.id === 'nav-id')

// Here it's the tag `

Now the nav variable is set to the closest element, matched with class name, id, or tag name. It's just a regular old reference to a DOM element, anything can be done with it.

This method is unbelievably light and versatile! It originates to this great SO answer.

Here's a little demo.

Pure JavaScript closest demo

What does closest to in JavaScript?

The closest() method in JavaScript is used to retrieve the closest ancestor, or parent of the element matches the selectors. If there is no ancestor found, the method returns null.

What does the element closest () method take as its argument and what does it return?

The closest() method searches up the DOM tree for elements which matches a specified CSS selector. The closest() method starts at the element itself, then the anchestors (parent, grandparent, ...) until a match is found. The closest() method returns null() if no match is found.

What is closest in jQuery?

The closest() method returns the first ancestor of the selected element. An ancestor is a parent, grandparent, great-grandparent, and so on. The DOM tree: This method traverse upwards from the current element, all the way up to the document's root element (), to find the first ancestor of DOM elements.

How do you select all ancestors of an element?

To select all ancestor elements of an element, the parents() method is used. This method is used to find all the parent elements related to the selected element. This method traverse all the levels up the selected element and return that all elements.