JavaScript: How to get parent element by selector?
Example:
<div someAttr="parentDiv. We need to get it from child.">
<table>
...
<td> <div id="myDiv"></div> </td>
...
</table>
</div>
I want to get the parent by some selector from the inner div element (the one with the myDiv
class).
How do I achieve that with plain JavaScript, without jQuery?
Something like:
var div = document.getElementById('myDiv');
div.someParentFindMethod('some selector');
You may use closest()
in modern browsers:
var div = document.querySelector('div#myDiv');
div.closest('div[someAtrr]');
Use object detection to supply a polyfill or alternative method for backwards compatability with IE.
Here's the most basic version:
function collectionHas(a, b) { //helper function (see below)
for(var i = 0, len = a.length; i < len; i ++) {
if(a[i] == b) return true;
}
return false;
}
function findParentBySelector(elm, selector) {
var all = document.querySelectorAll(selector);
var cur = elm.parentNode;
while(cur && !collectionHas(all, cur)) { //keep going up until you find a match
cur = cur.parentNode; //go up
}
return cur; //will return null if not found
}
var yourElm = document.getElementById("yourElm"); //div in your original code
var selector = ".yes";
var parent = findParentBySelector(yourElm, selector);
Finds the closest parent (or the element itself) that matches the given selector. Also included is a selector to stop searching, in case you know a common ancestor that you should stop searching at.
function closest(el, selector, stopSelector) {
var retval = null;
while (el) {
if (el.matches(selector)) {
retval = el;
break
} else if (stopSelector && el.matches(stopSelector)) {
break
}
el = el.parentElement;
}
return retval;
}
Using leech's answer with indexOf (to support IE)
This is using what leech talked about, but making it work for IE (IE doesn't support matches):
function closest(el, selector, stopSelector) {
var retval = null;
while (el) {
if (el.className.indexOf(selector) > -1) {
retval = el;
break
} else if (stopSelector && el.className.indexOf(stopSelector) > -1) {
break
}
el = el.parentElement;
}
return retval;
}
It's not perfect, but it works if the selector is unique enough so it won't accidentally match the incorrect element.
Here's a recursive solution:
function closest(el, selector, stopSelector) {
if(!el || !el.parentElement) return null
else if(stopSelector && el.parentElement.matches(stopSelector)) return null
else if(el.parentElement.matches(selector)) return el.parentElement
else return closest(el.parentElement, selector, stopSelector)
}