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

Here's a boil down on jQuery's .closest() functions 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.

Take following HTML:

            <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>

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 go like:

    .parent() // ul
    .parent() // nav
    .css("background-color", "red");

That’s not very DRY, closest is made just for these kinds of thing:

    .css("background-color", "red");

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

Pure JavaScript closestByClass function

Closest functionality is sure doable with pure JavaScript. Below is a function that gets the closest by class name. From it, you can see much more in detail how the closest function really works:

 * Get the closest element of a given element by class
 * Take an element (the first param), and traverse the DOM upward from it
 * until it hits the element with a given class name (second parameter).
 * This mimics jQuery's `.closest()`.
 * @param  {element} el    The element to start from
 * @param  {string}  clazz The class name
 * @return {element}       The closest element
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`

    // Then return the matched element
    return el;

Example usage:

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

That’s great, 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. Let’s look a more flexible, alas less intuitive, method.

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));

That’t pretty cool, and quite dense. Let’s look at the usage, take the example list we had earlier:

<div class="wrap" id="wrap">
    <nav class="nav" id="nav-id">
        <ul class="nav__list">
            <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>

Here’s a simple example, note how easy it is to target class, id, or tag name:

// Get the "starting point" element
var srcEl = document.getElementsByClassName('nav__item-3');

// The element with a class of `.nav` is the wanted element in this case
// The first parameter for the `closest()` is the starting point
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`

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

// Here it's the tag <nav>
var nav = closest(srcEl[0], function(el) {
    return 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 great SO answer.

Here’s a little demo.

Pure JavaScript closest demo

Use cases for closest

Toggling a hidden drawer with jQuery or Zepto

Look at the header of this blog, there’s a hotdog icon. Click the icons and a drawer opens, click away anywhere and it closes. The closing functionality uses the .closest().

Here’s my implementation of a nav hider/opener function:

 * Show a hidden element, hide it when clicking out of it
 * @param {string} trigger The button to show the element
 * @param {string} el      The element to toggle
var hiderShower = function(trigger, el) {
    // Cache elements for speed
    var $trigger = $(trigger),
        $el = $(el);
    // Listen the whole document for clicks
    $(document).on('click', function(event) {
        // If the trigger element is clicked, or
        // anything inside it, like the SVG cog icons.
        if ($(event.target).closest(trigger).length) {
        // If anything else than the trigger element is clicked
        } else if (!$(event.target).closest(el).length) {

Drawer function with pure JavaScript

Here’s a function that does the same thing, hides and shows a drawer, but it uses the minimalistic recursive method.

 * Toggles a drawer panel
 * @param  {string} trigger     Reference to button, id, class, or element
 * @param  {string} el          Reference to the panel element, id, class, or element
 * @param  {string} closestId   The ID of the closest element
 * @param  {string} activeClass Which class to toggle on the drawer element
var hiderShower = function(trigger, drawer, closestId, activeClass) {
    // Grab the elements and define the closest function
    var trigger = document.querySelector(trigger),
        drawer = document.querySelector(drawer),
        closest = function(elem, fn) {
            return elem && (fn(elem) ? elem : closest(elem.parentNode, fn));

    // Listen clicks in the document
    document.addEventListener('click', function(e) {
        // Get the closest
        var closestEl = closest(e.target, function(elem) {
            return elem.id === closestId;
        // If the trigger is clicked
        if (e.target.id === trigger.id) {
        // Close if anywhere else is clicked
        if (!closestEl) {

Here’s a sample HTML it might be used with:

<header class="column-header" id="header">
    <h1 class="column-header__h1">

    <!-- Hidden nav -->
    <button class="hotdog"></button>
    <nav class="overlay overlay--nav" id="overlay--nav">
        <ul class="large-bullets">
            <li class="categories">

Example usage:

hiderShower('#hotdog', '#overlay--nav', 'header', 'active');

Pros of this function: it’s really not that much larger than it’s jQuery counterpart. Cons: limited bowser support, classList is only supported in IE10, and addEventListener in IE9. I’m just drafting a post that includes a version with more vast browser support, will link it here when done.


The recursive closest is pretty epic and small, whereas it might be a bit unintuitive to understand. I have a followup post brewing that deals with the drawers more.


Club-Mate, the beverage → club-mate.fi