Find the closest ancestor element that has a specific class

How can I find the ancestor of an element that is closest to a tree having a specific class in pure JavaScript ? For example, in a tree:

<div class="far ancestor"> <div class="near ancestor"> <p>Where am I?</p> </div> </div> 

Then I want div.near.ancestor if I try this on p and search for ancestor .

+138
javascript dom html
Mar 01 '14 at 20:05
source share
5 answers

This is 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 parent every iteration, so at the end you have an ancestor with this class or null .

Here is the violin if someone wants to improve it. It will not work on older browsers (i.e. IE); see this compatibility table for classList . parentElement used here because parentNode will include more work to make sure the node is an element.

+171
Mar 01 '14 at 20:05
source share

Update: Now supported in most major browsers.

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

Note that this may correspond to 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() , you can build a selector mapping similar to the @rvighne class mapping:

 function findAncestor (el, sel) { while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel))); return el; } 
+199
Nov 20 '14 at 10:43
source share

Use element.closest ()

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

See this DOM example:

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

Here 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 
+19
Mar 27 '18 at 14:54
source share

Based on the answer from 8472 and https://developer.mozilla.org/en-US/docs/Web/API/Element/matches, here is a 2017 cross-platform 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; } 
+10
Mar 18 '17 at 10:22
source share
Decision

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

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

Of course, indexOf just looks for the presence of this row, it doesn't matter if it is the whole row or not. Therefore, if you have another element with the ancestor type class, it will still come back, since it found an ancestor, if this is a problem for you, you can probably use regexp to find the exact match.

+8
Jun 18 '17 at 13:02 on
source share



All Articles