40

I'm working on an application that only has jQuery 1.1 installed which dosn't support the .closest method.

My script currently looks like this:

$('.navPanel').find('img').closest('td').hide();

So I'm looking for every image in the .navPanel and traversing up the DOM and hiding the td that it sits in. Does anyone know if there is a vanilla JavaScript function I could simply add to my script that polyfills the missing .closest method?

Thanks for your time.

j08691
  • 204,283
  • 31
  • 260
  • 272
William Li
  • 471
  • 1
  • 4
  • 10
  • [`.parents()`](http://api.jquery.com/parents/)? – Artyom Neustroev Feb 09 '15 at 09:29
  • http://stackoverflow.com/questions/15329167/closest-ancestor-matching-selector-using-native-dom – BeNdErR Feb 09 '15 at 09:29
  • Hi, should have said that .parent() will not work in this instance as some of the pages, the img has a wrapping a href tag around it which will remove this element rather than the td. So it really needs to find the closest td – William Li Feb 09 '15 at 09:31
  • 2
    jQuery 1.1 is *over 8 years old*! Is there really no way you can update it? – Rory McCrossan Feb 09 '15 at 09:32
  • @WilliamLi There is a difference. `parent()` gives the direct parent. `parents()` will give you all parents, and in that list you can find the right one. – GolezTrol Feb 09 '15 at 09:35
  • Why is that application stuck at jQuery 1.1? – Cerbrus Feb 09 '15 at 09:39
  • Perhaps this will help, for vanilla JS: http://stackoverflow.com/questions/28348386/javascript-cut-table-to-insert-a-tag-div/28353551#28353551 – Xotic750 Feb 09 '15 at 09:41
  • Hi, thanks for all responses - the application has been developed offshore and I've been tasked with giving it a facelift. The developers are reluctant to upgrade jQuery due to the massive regression test that would be needed – William Li Feb 09 '15 at 09:42
  • 1
    "reluctant" or "lazy"! :D – Xotic750 Feb 09 '15 at 10:05
  • @ZakariaAcharki - Please stop tagging questions with `vanilla-js`. It's simply JavaScript. – j08691 Jan 26 '16 at 22:02

3 Answers3

84

Modern browsers have the Element.closest() method.

Example:

document.querySelector('.navPanel img').closest('td')

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

Here you can check which browsers have built-in support: https://caniuse.com/#feat=element-closest

When there is no support in the browser the following polyfill could be used: https://github.com/jonathantneal/closest

jpoppe
  • 2,228
  • 24
  • 25
  • 3
    This is the answer. Thank you much for teaching me things I do not know - and for providing a link to the polyfill. – jeremysawesome Jun 07 '18 at 14:52
  • 1
    This is not working in IE and Edge at the moment. The [source](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Browser_compatibility) – Dmitry Shvetsov Aug 23 '18 at 02:15
35
myImage.parentNode;

Is as vanilla as it gets. Whack that in a loop until you hit the required tag type:

while(thisTag.parentNode.tagName !== 'TD' && thisTag.parentNode != document) // uppercase in HTML, lower in XML
    {
     thisTag=thisTag.parentNode;
    }

That should do the trick in plain old js.

Danny

allnodcoms
  • 1,244
  • 10
  • 14
10

Cause IE still does not support document.querySelector('.element').closest() the simplest thing to do is include the polyfill from MDN Element.closest() . Include it at the very beginning of your javascript codes.

For browsers that do not support Element.closest(), but carry support for element.matches() (or a prefixed equivalent, meaning IE9+), include this polyfill:

if (!Element.prototype.matches) {
  Element.prototype.matches = Element.prototype.msMatchesSelector || 
                              Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
  Element.prototype.closest = function(s) {
    var el = this;

    do {
      if (Element.prototype.matches.call(el, s)) return el;
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1);
    return null;
  };
}

However, if you really do require IE 8 support, then the following polyfill will do the job very slowly, but eventually. However, it will only support CSS 2.1 selectors in IE 8, and it can cause severe lag spikes in production websites.

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;
  };
}
caramba
  • 21,963
  • 19
  • 86
  • 127