130

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');
janw
  • 8,758
  • 11
  • 40
  • 62
nkuhta
  • 10,388
  • 12
  • 41
  • 54
  • 4
    Google give only jQuery.parent everywhere. – nkuhta Jan 09 '13 at 15:10
  • Possible duplicate of [Find the closest ancestor element that has a specific class](http://stackoverflow.com/questions/22119673/find-the-closest-ancestor-element-that-has-a-specific-class) – Vadzim Dec 08 '16 at 14:54
  • 1
    Please update selected answer per suggestion from @soullivaneuh below. – vhs Sep 02 '18 at 09:54
  • Jquery closest for people who don't have the constraint of not using JQuery - https://api.jquery.com/closest/ – Sahil Singh Jun 13 '21 at 19:29
  • Note that this question is looking for **ancestor** elements matching a query; for anyone looking for only the **parent** element, try `element.parentNode` or if it needs to match a query, something like `element.parentNode.matches('.some-query') ? element.parentNode : null` – user56reinstatemonica8 Nov 29 '22 at 12:55

11 Answers11

352

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.

vhs
  • 9,316
  • 3
  • 66
  • 70
George Kargakis
  • 4,940
  • 3
  • 16
  • 12
47

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);
Chris
  • 26,544
  • 5
  • 58
  • 71
  • 24
    This is rather inefficient in that it retrieves all elements with that selector in the entire document, rather than just checking the parent. –  Oct 05 '16 at 12:58
  • 2
    This [JSPerf test](https://jsperf.com/matches-vs-closest) analyzes `Element.matches()`, `Element.closest()`, and one polyfill for each of those methods. The polyfill for `Element.matches()` looks very similar to your implementation. To address the performance concern of @user663031, perhaps you could use `Element.matches()` instead of `Document.querySelectorAll()`. It appears to run faster than the related polyfill. – Ryan V Mar 03 '20 at 18:16
  • Agree with user663031 – Don Nisnoni Apr 12 '20 at 20:02
44

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;
}
leech
  • 8,293
  • 7
  • 62
  • 78
  • 1
    closest is a shitty name, should be named "closestAncestor" , if the closest ancestor is 10 elements up and the closest descendant is 1 child down, the "closest" will be the descendant, not the ancestor, but this function will return the ancestor instead – hanshenrik Aug 03 '21 at 11:50
7

By using querySelector() and closest() methods is possible to get the parent element.

  • querySelector() returns the first element that matches a specified CSS selector(s) in the document.

  • closest() searches up the DOM tree for the closest element which matches a specified CSS selector.

Usage example

const element = document.querySelector('td')

console.log(element.closest('div'))
<div>
  <table>
    <tr>
      <td></td>
    </tr>
  </table>
</div>

In case of needing to select more than one element, querySelectorAll() is a good fit.

  • querySelectorAll() returns all elements in the document that matches a specified CSS selector(s), as a static NodeList object.

To select the desired element, is necessary to specify it inside [] so, for example for the second element would be: element[1]

In the following example closest() method is used to get the parent <tr> element of an specific selected element.

const el = document.querySelectorAll('td')

console.log(el[1].closest('tr'))
<div>
  <table>
    <tr id="1">
      <td> First text </td>
    </tr>
    <tr id="2">
      <td> Second text </td>
    </tr>
  </table>
</div>
Gass
  • 7,536
  • 3
  • 37
  • 41
2

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.

Trevor Nestman
  • 2,456
  • 19
  • 26
2

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)
}
Cully
  • 6,427
  • 4
  • 36
  • 58
2

I thought I would provide a much more robust example, also in typescript, but it would be easy to convert to pure javascript. This function will query parents using either the ID like so "#my-element" or the class ".my-class" and unlike some of these answers will handle multiple classes. I found I named some similarly and so the examples above were finding the wrong things.

function queryParentElement(el:HTMLElement | null, selector:string) {
    let isIDSelector = selector.indexOf("#") === 0
    if (selector.indexOf('.') === 0 || selector.indexOf('#') === 0) {
        selector = selector.slice(1)
    }
    while (el) {
        if (isIDSelector) {
            if (el.id === selector) {
                return el
            }
        }
        else if (el.classList.contains(selector)) {
            return el;
        }
        el = el.parentElement;
    }
    return null;
}

To select by class name:

let elementByClassName = queryParentElement(someElement,".my-class")

To select by ID:

let elementByID = queryParentElement(someElement,"#my-element")

Joel Teply
  • 3,260
  • 1
  • 31
  • 21
1

This is a simple way using recursion to get the parent with a specific class. You can modify it to switch depending on the selector, but the principle is the same:

function findParentByClass(child, parentClass) {
    let parent = child.parentElement;

    while(!(parent.className === parentClass)){
        parent = findParentByClass(child.parentElement, parentClass)
    }

    return parent;
}
KarlsMaranjs
  • 311
  • 1
  • 9
0

simple example of a function parent_by_selector which return a parent or null (no selector matches):

function parent_by_selector(node, selector, stop_selector = 'body') {
  var parent = node.parentNode;
  while (true) {
    if (parent.matches(stop_selector)) break;
    if (parent.matches(selector)) break;
    parent = parent.parentNode; // get upper parent and check again
  }
  if (parent.matches(stop_selector)) parent = null; // when parent is a tag 'body' -> parent not found
  return parent;
};
Sergio Belevskij
  • 2,478
  • 25
  • 24
-3

Here is simple way to access parent id

document.getElementById("child1").parentNode;

will do the magic for you to access the parent div.

<html>
<head>
</head>
<body id="body">
<script>
function alertAncestorsUntilID() {
var a = document.getElementById("child").parentNode;
alert(a.id);
}
</script>
<div id="master">
Master
<div id="child">Child</div>
</div>
<script>
alertAncestorsUntilID();
</script>
</body>
</html>
ameya rote
  • 1,116
  • 1
  • 9
  • 13
-4
var base_element = document.getElementById('__EXAMPLE_ELEMENT__');
for( var found_parent=base_element, i=100; found_parent.parentNode && !(found_parent=found_parent.parentNode).classList.contains('__CLASS_NAME__') && i>0; i-- );
console.log( found_parent );