292

How can I check if one DOM element is a child of another DOM element? Are there any built in methods for this? For example, something like:

if (element1.hasDescendant(element2)) 

or

if (element2.hasParent(element1)) 

If not then any ideas how to do this? It also needs to be cross browser. I should also mention that the child could be nested many levels below the parent.

B T
  • 57,525
  • 34
  • 189
  • 207
AJ.
  • 10,732
  • 13
  • 41
  • 50

10 Answers10

440

You should use Node.contains, since it's now standard and available in all browsers.

https://developer.mozilla.org/en-US/docs/Web/API/Node.contains

Glen Selle
  • 3,966
  • 4
  • 37
  • 59
Brian Di Palma
  • 7,043
  • 3
  • 19
  • 15
  • The Node.hasParent(parent) method is unnecessary but would be Node.prototype.hasParent=function(element){return element.contains(this);}; – llange Jul 09 '14 at 10:20
  • 12
    you could've at least added an example – The Onin Apr 05 '18 at 06:25
  • 5
    This is good answear but example would be real nice: `var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }` – baron_bartek Jul 09 '19 at 07:57
  • Offcourse you need to loop throu elements if you need to check all of them: for (var i=0;i – baron_bartek Jul 09 '19 at 08:16
  • Yeah I'd say the mozilla "example" out there clears up nothing, explicitly returns vs implicitly returns? Bunch of gobblygook rather than an actual example: https://developer.mozilla.org/en-US/docs/Web/API/Node/contains – gseattle Apr 04 '21 at 20:08
  • One gotcha I didn't see mentioned is that `Node.contains` includes the node itself. So if you truly want to check whether a node is the child of another node, you also need to check that it's not the original node: `parentNode.contains(childNode) && parentNode !== childNode`. – ericgio Feb 16 '22 at 20:56
297

Update: There's now a native way to achieve this. Node.contains(). Mentioned in comment and below answers as well.

Old answer:

Using the parentNode property should work. It's also pretty safe from a cross-browser standpoint. If the relationship is known to be one level deep, you could check it simply:

if (element2.parentNode == element1) { ... }

If the the child can be nested arbitrarily deep inside the parent, you could use a function similar to the following to test for the relationship:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}
Anna
  • 319
  • 5
  • 18
Asaph
  • 159,146
  • 25
  • 197
  • 199
  • 1
    Thanks for your reply, the problem is that the child could be nested many levels below the parent. – AJ. Feb 10 '10 at 07:01
  • @AJ: I updated my answer to include a solution that should work for nesting the child arbitrarily deep in the parent. – Asaph Feb 10 '10 at 07:09
  • 254
    If anyone is coming to this now it may be possible for you to use Node.contains (https://developer.mozilla.org/en-US/docs/DOM/Node.contains) which is a native function in modern browsers. – Adam Heath Jan 07 '13 at 07:31
  • I [posted a simple one-line variation](http://stackoverflow.com/a/18162093/588079) (neither adding or omitting functionality compared to your answer) as answer, because properly explaining that line is unfeasible as a comment. – GitaarLAB Aug 10 '13 at 13:31
  • 2
    @JohnCarrell There is `Node.contains` (no caniuse page though) – gcampbell Sep 25 '16 at 09:10
  • 3
    @Asaph Could you please update your answer to include the new `Node.contains`? :) –  Jan 02 '17 at 11:30
  • This works but is useless because their is a native way: `parent.contains(child)` – Yukulélé Sep 19 '17 at 09:59
  • @AdamHeath you should propose this as another answer – wallop Jun 06 '19 at 16:54
49

I just had to share 'mine'.

Although conceptually the same as Asaph's answer (benefiting from the same cross-browser compatibility, even IE6), it is a lot smaller and comes in handy when size is at a premium and/or when it is not needed so often.

function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
  while((c=c.parentNode)&&c!==p); 
  return !!c; 
}

..or as one-liner (just 64 chars!):

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

and jsfiddle here.


Usage:
childOf(child, parent) returns boolean true|false.

Explanation:
while evaluates as long as the while-condition evaluates to true.
The && (AND) operator returns this boolean true/false after evaluating the left-hand side and the right-hand side, but only if the left-hand side was true (left-hand && right-hand).

The left-hand side (of &&) is: (c=c.parentNode).
This will first assign the parentNode of c to c and then the AND operator will evaluate the resulting c as a boolean.
Since parentNode returns null if there is no parent left and null is converted to false, the while-loop will correctly stop when there are no more parents.

The right-hand side (of &&) is: c!==p.
The !== comparison operator is 'not exactly equal to'. So if the child's parent isn't the parent (you specified) it evaluates to true, but if the child's parent is the parent then it evaluates to false.
So if c!==p evaluates to false, then the && operator returns false as the while-condition and the while-loop stops. (Note there is no need for a while-body and the closing ; semicolon is required.)

So when the while-loop ends, c is either a node (not null) when it found a parent OR it is null (when the loop ran through to the end without finding a match).

Thus we simply return that fact (converted as boolean value, instead of the node) with: return !!c;: the ! (NOT operator) inverts a boolean value (true becomes false and vice-versa).
!c converts c (node or null) to a boolean before it can invert that value. So adding a second ! (!!c) converts this false back to true (which is why a double !! is often used to 'convert anything to boolean').


Extra:
The function's body/payload is so small that, depending on case (like when it is not used often and appears just once in the code), one could even omit the function (wrapping) and just use the while-loop:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

c=a; while((c=c.parentNode)&&c!==b); //c=!!c;

if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
    //do stuff
}

instead of:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

c=childOf(a, b);    

if(c){ 
    //do stuff
}
Community
  • 1
  • 1
GitaarLAB
  • 14,536
  • 11
  • 60
  • 80
  • 4
    @SolomonUcko: The strict equality comparison (`!==`) can improve speed by making it clear to the compiler that it can skip type-checking and optional implicit conversion steps that would occur in a loose equality comparison, thus improving speed (by more accurately describing what we want to happen, which is also beneficial to the programmer). I also seem to recall from when I wrote this, that just the loose comparison (`!=`) was either very error-prone or didn't work at all in some older browser(s) (I suspect it was in IE6, but I have forgotten). – GitaarLAB May 12 '16 at 07:59
32

Another solution that wasn't mentioned:

Example Here

var parent = document.querySelector('.parent');

if (parent.querySelector('.child') !== null) {
    // .. it's a child
}

It doesn't matter whether the element is a direct child, it will work at any depth.


Alternatively, using the .contains() method:

Example Here

var parent = document.querySelector('.parent'),
    child = document.querySelector('.child');

if (parent.contains(child)) {
    // .. it's a child
}
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
  • Your first proposed solution has very bad performance without necessity: The querySelector('.child') has to traverse the entire subtree below parent to collect all matching children. Traversing from a known child element upwards is way faster. – Martin Lisowski Jul 04 '23 at 11:32
  • @MartinLisowski — look at the performance benchmarks. You are wrong. – Josh Crozier Jul 04 '23 at 22:54
25

You can use the contains method

var result = parent.contains(child);

or you can try to use compareDocumentPosition()

var result = nodeA.compareDocumentPosition(nodeB);

The last one is more powerful: it return a bitmask as result.

Fez Vrasta
  • 14,110
  • 21
  • 98
  • 160
Iegor Kozakov
  • 351
  • 3
  • 3
22

Take a look at Node#compareDocumentPosition.

function isDescendant(ancestor,descendant){
    return ancestor.compareDocumentPosition(descendant) & 
        Node.DOCUMENT_POSITION_CONTAINS;
}

function isAncestor(descendant,ancestor){
    return descendant.compareDocumentPosition(ancestor) & 
        Node.DOCUMENT_POSITION_CONTAINED_BY;
}

Other relationships include DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDING, and DOCUMENT_POSITION_FOLLOWING.

Not supported in IE<=8.

  • interesting! This reminds me of a JS function I developed on year 2003... Took me some days and got help of some JS developers... Incredible (and sad) that nowadays there's still no full drag-drop nor object-fit full browser implementation. – DavidTaubmann Nov 06 '16 at 18:18
4

Consider using closest('.selector')

It returns null if neither element nor any of its ancestors matches the selector. Alternatively returns the element which was found

helmax
  • 41
  • 2
3

I came across a wonderful piece of code to check whether or not an element is a child of another element. I have to use this because IE doesn't support the .contains element method. Hope this will help others as well.

Below is the function:

function isChildOf(childObject, containerObject) {
  var returnValue = false;
  var currentObject;

  if (typeof containerObject === 'string') {
    containerObject = document.getElementById(containerObject);
  }
  if (typeof childObject === 'string') {
    childObject = document.getElementById(childObject);
  }

  currentObject = childObject.parentNode;

  while (currentObject !== undefined) {
    if (currentObject === document.body) {
      break;
    }

    if (currentObject.id == containerObject.id) {
      returnValue = true;
      break;
    }

    // Move up the hierarchy
    currentObject = currentObject.parentNode;
  }

  return returnValue;
}
Hitesh Surani
  • 12,733
  • 6
  • 54
  • 65
RashFlash
  • 992
  • 2
  • 20
  • 40
1

try this one:

x = document.getElementById("td35");
if (x.childElementCount > 0) {
    x = document.getElementById("LastRow");
    x.style.display = "block";
}
else {
    x = document.getElementById("LastRow");
    x.style.display = "none";
}
0

TL;DR: a library

I advise using something like dom-helpers, written by the react team as a regular JS lib.

In their contains implementation you will see a Node#contains based implementation with a Node#compareDocumentPosition fallback.

Support for very old browsers e.g. IE <9 would not be given, which I find acceptable.

This answer incorporates the above ones, however I would advise against looping yourself.

eljefedelrodeodeljefe
  • 6,304
  • 7
  • 29
  • 61