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.

Axel
  • 3,331
  • 11
  • 35
  • 58
P5music
  • 3,197
  • 2
  • 32
  • 81
  • `element.parentNode` ? https://developer.mozilla.org/en-US/docs/DOM/Node.parentNode – James Oct 12 '12 at 17:39
  • 1
    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. – Marc B Oct 12 '12 at 17:40
  • 4
    If you have questions regarding DOM structure, then please format your HTML so that the structure is easily recognisable. Thank you! – Felix Kling Oct 12 '12 at 17:40
  • 1
    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. – P5music Oct 12 '12 at 17:50

3 Answers3

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));
I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
  • 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? – P5music Oct 12 '12 at 18:25
  • @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. – I Hate Lazy Oct 15 '12 at 01:19
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.

Grayson
  • 146
  • 4
  • `n.parentNode.tagName == 'body'`: Why do you require the parent of the required element to be `body`? – John Dvorak Oct 12 '12 at 18:06
  • 2
    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. – I Hate Lazy Oct 12 '12 at 18:12
  • I had no idea that JS would see whitespace as additional text nodes. Thanks! – Grayson Oct 12 '12 at 18:29
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.

Community
  • 1
  • 1
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 2
    Thank you. I know jQuery is very appreciated but this problem seems not to need it, so why use it? – P5music Oct 12 '12 at 18:47