Finding closest element without jQuery
Solution 1:
Very simple:
el.closest('tbody')
Supported on all browsers except IE.
UPDATE: Edge now support it as well.
No need for jQuery.
More over, replacing jQuery's $(this).closest('tbody')
with $(this.closest('tbody'))
will increase performance, significantly when the element is not found.
Polyfill for IE:
if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
var el = this;
while (el) {
if (el.matches(selector)) {
return el;
}
el = el.parentElement;
}
};
Note that there's no return
when the element was not found, effectively returning undefined
when the closest element was not found.
For more details see: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
Solution 2:
Little (very) late to the party, but nonetheless. This should do the trick:
function closest(el, selector) {
var matchesFn;
// find vendor prefix
['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
if (typeof document.body[fn] == 'function') {
matchesFn = fn;
return true;
}
return false;
})
var parent;
// traverse parents
while (el) {
parent = el.parentElement;
if (parent && parent[matchesFn](selector)) {
return parent;
}
el = parent;
}
return null;
}
Solution 3:
Here's how you get the closest element by tag name without jQuery:
function getClosest(el, tag) {
// this is necessary since nodeName is always in upper case
tag = tag.toUpperCase();
do {
if (el.nodeName === tag) {
// tag name is found! let's return it. :)
return el;
}
} while (el = el.parentNode);
// not found :(
return null;
}
getClosest(th, 'tbody');
Solution 4:
There exists a standardised function to do this: Element.closest. Most browsers except IE11 support it (details by caniuse.com). The MDN docs also include a polyfill in case you have to target older browsers.
To find the closest tbody
parent given a th
you could do:
th.closest('tbody');
In case you want to write the function yourself - here is what I came up with:
function findClosestParent (startElement, fn) {
var parent = startElement.parentElement;
if (!parent) return undefined;
return fn(parent) ? parent : findClosestParent(parent, fn);
}
To find the closest parent by tag name you could use it like this:
findClosestParent(x, element => return element.tagName === "SECTION");
Solution 5:
function closest(el, sel) {
if (el != null)
return el.matches(sel) ? el
: (el.querySelector(sel)
|| closest(el.parentNode, sel));
}
This solution uses some of the more recent features of the HTML 5 spec, and using this on older/incompatible browsers (read: Internet Explorer) will require a polyfill.
Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector
|| Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector
|| Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);