javascriptjquerystandardsw3cdom-traversal

Closest ancestor matching selector using native DOM?


Is anybody working on a jQuery.closest() equivalent in the DOM api?

Looks like the Selectors Level 2 draft adds matches() equivalent to jQuery.is(), so native closest should be much easier to write. Has adding closest() to Selectors come up?


Solution

  • See the element.closest() documentation.

    Implementing such function with Element.matches() seems not optimal in terms of performance, cause apparently matches() will make a call to querySelectorAll() every time you test a parent, while only one call is sufficient for the job.

    Here's a polyfill for closest() on MDN. Note a single call to querySelectorAll()

    if (window.Element && !Element.prototype.closest) {
      Element.prototype.closest = 
      function(s) {
          var matches = (this.document || this.ownerDocument).querySelectorAll(s),
              i,
              el = this;
          do {
              i = matches.length;
              while (--i >= 0 && matches.item(i) !== el) {};
          } while ((i < 0) && (el = el.parentElement)); 
          return el;
      };
    }
    

    But bear in mind that function implemented like this will not work properly on unattached tree (detached from document.documentElement root)

    //Element.prototype.closestTest = function(s){...as seen above...};
    
    var detachedRoot = document.createElement("footer");
    var child = detachedRoot.appendChild(document.createElement("div"));
    detachedRoot.parentElement; //null
    
    child.closestTest("footer"); //null
    
    document.documentElement.append(detachedRoot);
    child.closestTest("footer"); //<footer>   
    

    Though closest() that is implemented in Firefox 51.0.1 seems to work fine with detached tree

    document.documentElement.removeChild(detachedRoot);
    child.closestTest("footer"); //null
    child.closest("footer"); //<footer>