# jQuery's .closest() function, and pure JavaScript alternatives

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:

```html
<div>
  <nav>
    <ul>
      <li class="nav-item-1">item1</li>
      <li class="nav-item-2">item2</li>
      <li class="nav-item-3">item3</li>
      <li class="nav-item-4">item4</li>
    </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:

```js
$('.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:

```js
$('.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:

```js
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:

```js
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`. Although 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:

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

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

```js
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:

```js
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 `<nav>`, and so on...
var nav = closest(srcEl[0], el => el.tagName.toLowerCase() === 'nav')
```

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](https://stackoverflow.com/a/22101242/976972) great SO answer.

Here's a little demo.

[Pure JavaScript closest demo](http://jsbin.com/xizako/2/embed?js,output)[script](http://static.jsbin.com/js/embed.js)
