Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the closest ancestor element that has a specific class

People also ask

How do I find my nearest parent by class?

Use the closest() method to get the closest parent element by class, e.g. child. closest('. parent') . The closest() method traverses the Element and its parents until it finds a node that matches the provided selector.

How do you select an element with the class name?

class selector selects elements with a specific class attribute. To select elements with a specific class, write a period (.) character, followed by the name of the class. You can also specify that only specific HTML elements should be affected by a class.

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.

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.


Update: Now supported in most major browsers

document.querySelector("p").closest(".near.ancestor")

Note that this can match selectors, not just classes

https://developer.mozilla.org/en-US/docs/Web/API/Element.closest


For legacy browsers that do not support closest() but have matches() one can build selector-matching similar to @rvighne's class matching:

function findAncestor (el, sel) {
    while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
    return el;
}

This does the trick:

function findAncestor (el, cls) {
    while ((el = el.parentElement) && !el.classList.contains(cls));
    return el;
}

The while loop waits until el has the desired class, and it sets el to el's parent every iteration so in the end, you have the ancestor with that class or null.

Here's a fiddle, if anyone wants to improve it. It won't work on old browsers (i.e. IE); see this compatibility table for classList. parentElement is used here because parentNode would involve more work to make sure that the node is an element.


Use element.closest()

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

See this example DOM:

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>

This is how you would use element.closest:

var el = document.getElementById('div-03');

var r1 = el.closest("#div-02");  
// returns the element with the id=div-02

var r2 = el.closest("div div");  
// returns the closest ancestor which is a div in div, here is div-03 itself

var r3 = el.closest("article > div");  
// returns the closest ancestor which is a div and has a parent article, here is div-01

var r4 = el.closest(":not(div)");
// returns the closest ancestor which is not a div, here is the outmost article

Based on the the8472 answer and https://developer.mozilla.org/en-US/docs/Web/API/Element/matches here is cross-platform 2017 solution:

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}

function findAncestor(el, sel) {
    if (typeof el.closest === 'function') {
        return el.closest(sel) || null;
    }
    while (el) {
        if (el.matches(sel)) {
            return el;
        }
        el = el.parentElement;
    }
    return null;
}

@rvighne solution works well, but as identified in the comments ParentElement and ClassList both have compatibility issues. To make it more compatible, I have used:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNode property instead of the parentElement property
  • indexOf method on the className property instead of the contains method on the classList property.

Of course, indexOf is simply looking for the presence of that string, it does not care if it is the whole string or not. So if you had another element with class 'ancestor-type' it would still return as having found 'ancestor', if this is a problem for you, perhaps you can use regexp to find an exact match.