How to get the parent node of an element when the parent has siblings?
Asked Answered
H

3

12

Please take a look at the snippet below:

<div>
    <div></div>
    <div><!-- my target node -->
        <div><!-- not my target node -->
            <img /><!-- my source node -->
        </div>
    </div>
</div>

As you can see the img-elment has two enclosing divs. I want the first of those two enclosing divs to be considered the "real" parent (the one I need to find) of the img-elment because it has a brother div before so the search ends and the brother div and the outer enclosing div are ignored.

In the case there are no siblings at all, the outer div has to be yielded; in the case the element is not enclosed, the element itself has to be yielded.

I just would like to know how to target the element as I explained via JavaScript.

Hyperpyrexia answered 12/10, 2012 at 17:37 Comment(4)
element.parentNode ? developer.mozilla.org/en-US/docs/DOM/Node.parentNodeGeneralship
dom elements only ever have one parent. Get the img node, and you can trivially get its parent with .parentNode. in fact, you can use .parentNode to follow a node branch all the way up to the root of the tree.Teller
If you have questions regarding DOM structure, then please format your HTML so that the structure is easily recognisable. Thank you!Voltaism
I need to find the parent that has brothers (or has body as its parent). So just the first one of the two enclosing divs for the img is suitable for me, because the second one (the inner one) has no brothers so it is not useful to me.Hyperpyrexia
P
21

So it sounds like you want the first ancestor that has siblings elements. If so, you can do it like this:

var parent = img.parentNode;

while (parent && !parent.previousElementSibling && !parent.nextElementSibling) {
    parent = parent.parentNode;
}

Or perhaps more appropriately written as a do-while loop:

do {
    var parent = img.parentNode;
} while (parent && !parent.previousElementSibling && !parent.nextElementSibling);

So the loop will end when it finds one with at least one sibling element, or when it runs out of ancestors.

If you know if the sibling comes before or after the parent, you can just test for one or the other.


Also note that you'll need a shim for the ***ElementSibling properties if you're supporting legacy browsers.

You can make a function that will do this:

function prevElement(el) {
    while ((el = el.previousSibling) && el.nodeType !== 1) {
        // nothing needed here
    }

    return el;
}

function nextElement(el) {
    while ((el = el.nextSibling) && el.nodeType !== 1) {
        // nothing needed here
    }

    return el;
}

Then use the functions like this:

do {
    var parent = img.parentNode;
} while (parent && !prevElement(parent) && !nextElement(parent));
Patois answered 12/10, 2012 at 17:52 Comment(2)
1) does "run out of ancestors" mean that eventually the body element can be yielded as a result if there are not siblings? 2) is your comment (to another answer) about possible whitespace formatting siblings relevant to my problem?Hyperpyrexia
@P5music: 1) Yes, it could. You can add a constraint to a specific node name. 2) It is relevant to the solution he provided. The whitespace formatting makes a text node. If I had used nextSibling instead of nextElementSibling, then that whitespace text node would have counted as a sibling.Patois
S
0

If you don't know how many levels up the parent element is, it will be difficult to select it using methods like element.getParent alone. However, you CAN iterate through parent nodes until the node you're looking at has siblings and is the child of a body element. Let's assume that your img tag is referred to by imgNode.

function getParentWithSiblings(imgNode) {
    for( ; n; n = imgNode.parentNode) {
        if (n.nextSibling && n.parentNode.tagName == 'body') {
            return n;
       }
    }
}

In the code above, we progressively iterate through the parents of the image node. At each iteration, we check whether the current node (some parent of the img node) has a sibling and is the child of a body tag.

Secunda answered 12/10, 2012 at 18:4 Comment(3)
n.parentNode.tagName == 'body': Why do you require the parent of the required element to be body?Bursary
You can't rely on .nextSibling alone to search for sibling elements. There will be text nodes on either side of any element that includes whitespace formatting (tab and newline characters), and .nextSibling will include those.Patois
I had no idea that JS would see whitespace as additional text nodes. Thanks!Secunda
A
0

Just in case you're curious, here's how you might implement user1689607's answer using jQuery.

function getAncestorWithSiblings(element) {
  var ancestor = element.parent();
  while (ancestor && ancestor.siblings().length === 0) {
    ancestor = ancestor.parent();
  }
  return ancestor;
}

Whether it makes sense to use this library for your purposes depends on a great deal of context we don't have. As others have rightfully pointed out, you don't need jQuery to solve this problem, and it may be an unnecessarily heavyweight solution. That said, it can be a very useful library and is certainly worth your consideration if you weren't aware of it or hadn't already looked into it.

Annulus answered 12/10, 2012 at 18:34 Comment(1)
Thank you. I know jQuery is very appreciated but this problem seems not to need it, so why use it?Hyperpyrexia

© 2022 - 2024 — McMap. All rights reserved.