133

For some performance reasons, I am trying to find a way to select only sibling nodes of the selected node.

For example,

<div id="outer">
  <div id="inner1"></div>
  <div id="inner2"></div>
  <div id="inner3"></div>
  <div id="inner4"></div>
</div>

If I selected inner1 node, is there a way for me to access its siblings, inner2-4 nodes?

Penny Liu
  • 11,885
  • 5
  • 66
  • 81
codingbear
  • 14,173
  • 20
  • 45
  • 64

15 Answers15

169

Well... sure... just access the parent and then the children.

 node.parentNode.childNodes[]

or... using jQuery:

$('#innerId').siblings()

Edit: Cletus as always is inspiring. I dug further. This is how jQuery gets siblings essentially:

function getChildren(n, skipMe){
    var r = [];
    for ( ; n; n = n.nextSibling ) 
       if ( n.nodeType == 1 && n != skipMe)
          r.push( n );        
    return r;
};

function getSiblings(n) {
    return getChildren(n.parentNode.firstChild, n);
}
cgp
  • 40,456
  • 11
  • 100
  • 131
  • 29
    Note that jquery.siblings() excludes the current node from the result set. – cletus May 09 '09 at 00:14
  • 3
    Which is a very nice feature! – cgp May 09 '09 at 00:15
  • 1
    Actually the equivalent of node.parentNode.childNodes[] in jquery is really $(node).parent().children() not $(node).siblings(). It can be a nice feature but it can also be annoying. It depends on what you want. – cletus May 09 '09 at 00:20
  • 1
    You'll wanna check for ELEMENT_NODE nodes with node.parentNode.childNodes, cause it will give you white space text nodes too. – seanmonstar May 09 '09 at 00:30
  • Nothing, I think it was a goof -- sorry about that (removed) – cgp Feb 20 '15 at 16:19
  • it is bad. only gets siblings that are next to your element using nextSibling. what about when your element is bang in the middle of the element group and you'd want to get all of the siblings. – Maciej Sitko Aug 07 '16 at 07:57
  • @MaciejSitko That's why it calls n. parentNode. **firstChild** before doing the nextSibling loop. – Venryx Mar 13 '19 at 00:12
119
var sibling = node.nextSibling;

This will return the sibling immediately after it, or null no more siblings are available. Likewise, you can use previousSibling.

[Edit] On second thought, this will not give the next div tag, but the whitespace after the node. Better seems to be

var sibling = node.nextElementSibling;

There also exists a previousElementSibling.

parvus
  • 5,310
  • 6
  • 34
  • 59
  • 1
    next/previousElementSibling isn't supported in IE8 – Vadim Sep 11 '14 at 08:03
  • 1
    Starting from IE9 it is. See also http://stackoverflow.com/questions/5197825/parentnode-or-previouselementsibling-not-working-in-ie8 – parvus Jun 10 '16 at 12:51
23

Quick:

var siblings = n => [...n.parentElement.children].filter(c=>c!=n)

https://codepen.io/anon/pen/LLoyrP?editors=1011

Get the parent's children as an array, filter out this element.

Edit:

And to filter out text nodes (Thanks pmrotule):

var siblings = n => [...n.parentElement.children].filter(c=>c.nodeType == 1 && c!=n)
roundar
  • 1,343
  • 1
  • 16
  • 22
14

From 2017:
straightforward answer: element.nextElementSibling for get the right element sibling. also you have element.previousElementSibling for previous one

from here is pretty simple to got all next sibiling

var n = element, ret = [];
while (n = n.nextElementSibling){
  ret.push(n)
}
return ret;
pery mimon
  • 6,650
  • 6
  • 45
  • 51
  • It should return all siblings, not only `next` or `previous`. Specifying exactly going backward or forward does not help much in this case. – dvlden Sep 23 '17 at 21:39
  • 4
    why not. if you take next and previous you can get the whole picture. that's answer comes to show a new way to do things. and contribute something to readers. just to go parent and go each element and filter the current one everyone can do – pery mimon Sep 24 '17 at 15:08
8

have you checked the "Sibling" method in jQuery?

    sibling: function( n, elem ) {
        var r = [];

        for ( ; n; n = n.nextSibling ) {
            if ( n.nodeType === 1 && n !== elem ) {
                r.push( n );
            }
        }

        return r;
    }

the n.nodeType == 1 check if the element is a html node and n!== exclude the current element.

I think you can use the same function, all that code seems to be vanilla javascript.

Nicolas Rojo
  • 653
  • 1
  • 6
  • 14
5

There are a few ways to do it.

Either one of the following should do the trick.

// METHOD A (ARRAY.FILTER, STRING.INDEXOF)
var siblings = function(node, children) {
    siblingList = children.filter(function(val) {
        return [node].indexOf(val) != -1;
    });
    return siblingList;
}

// METHOD B (FOR LOOP, IF STATEMENT, ARRAY.PUSH)
var siblings = function(node, children) {
    var siblingList = [];
    for (var n = children.length - 1; n >= 0; n--) {
        if (children[n] != node) {
            siblingList.push(children[n]);
        }  
    }
    return siblingList;
}

// METHOD C (STRING.INDEXOF, ARRAY.SPLICE)
var siblings = function(node, children) {
   siblingList = children;
   index = siblingList.indexOf(node);
   if(index != -1) {
       siblingList.splice(index, 1);
   }
   return siblingList;
}

FYI: The jQuery code-base is a great resource for observing Grade A Javascript.

Here is an excellent tool that reveals the jQuery code-base in a very streamlined way. http://james.padolsey.com/jquery/

abbotto
  • 4,085
  • 2
  • 20
  • 19
4

The following function will return an array containing all the siblings of the given element.

const getSiblings = node => [...node.parentNode.children].filter(c => c !== node)

// get "c" element siblings (excluding itself)
const siblingsToC = getSiblings(document.querySelector('.c'))

console.log( siblingsToC )
<ul>
  <li class='a'>a</li>
  <li class='b'>b</li>
  <li class='c'>c</li>
  <li class='d'>d</li>
  <li class='e'>e</li>
</ul>

Just pass the selected element into the getSiblings() function as it's only parameter.

vsync
  • 103,437
  • 51
  • 275
  • 359
Saabbir
  • 458
  • 5
  • 8
2

Use document.querySelectorAll() and Loops and iteration

function sibblingOf(children,targetChild){
  var children = document.querySelectorAll(children);
  for(var i=0; i< children.length; i++){
    children[i].addEventListener("click", function(){
      for(var y=0; y<children.length;y++){children[y].classList.remove("target")}
      this.classList.add("target")
    }, false)
  }
}

sibblingOf("#outer >div","#inner2");
#outer >div:not(.target){color:red}
<div id="outer">
      <div id="inner1">Div 1 </div>
      <div id="inner2">Div 2 </div>
      <div id="inner3">Div 3 </div>
      <div id="inner4">Div 4 </div>
 </div>
Reggie Pinkham
  • 10,715
  • 3
  • 35
  • 34
Gildas.Tambo
  • 21,175
  • 7
  • 48
  • 76
2

Here's how you could get previous, next and all siblings (both sides):

function prevSiblings(target) {
   var siblings = [], n = target;
   while(n = n.previousElementSibling) siblings.push(n);
   return siblings;
}

function nextSiblings(target) {
   var siblings = [], n = target;
   while(n = n.nextElementSibling) siblings.push(n);
   return siblings;
}

function siblings(target) {
    var prev = prevSiblings(target) || [],
        next = nexSiblings(target) || [];
    return prev.concat(next);
}
Maciej Sitko
  • 416
  • 2
  • 13
1

jQuery

$el.siblings();

Native - latest, Edge13+

[...el.parentNode.children].filter((child) =>
  child !== el
);

Native (alternative) - latest, Edge13+

Array.from(el.parentNode.children).filter((child) =>
  child !== el
);

Native - IE10+

Array.prototype.filter.call(el.parentNode.children, (child) =>
  child !== el
);
Humoyun Ahmad
  • 2,647
  • 3
  • 25
  • 41
0

1) Add selected class to target element
2) Find all children of parent element excluding target element
3) Remove class from target element

 <div id = "outer">
            <div class="item" id="inner1">Div 1 </div>
            <div class="item" id="inner2">Div 2 </div>
            <div class="item" id="inner3">Div 3 </div>
            <div class="item" id="inner4">Div 4 </div>
           </div>



function getSiblings(target) {
    target.classList.add('selected');
    let siblings = document.querySelecttorAll('#outer .item:not(.currentlySelected)')
    target.classList.remove('selected'); 
return siblings
    }
Manoj
  • 1,155
  • 7
  • 11
0

You can access the following sibling nodes, with the currentNode.nextSibiling property.

This is how you can do in the event delegation way, which is a dynamic way to add event listeners

 document.addEventListener('click', (event) => {
   if (event.target.matches("#inner1")) {
    console.log(event.targert.nextSibling); //inner2 div
    console.log(event.targert.nextSibling.nextSibling); //inner3 div 
    /* The more of the property you keep appending the further it goes to 
    the next sibling */
   }
 })
Dan
  • 93
  • 1
  • 9
0

My use case was different. I had to select a few spans which didn't have any id/classes (nor their parents), just an entry point (#target). Once you have that, run a querySelectorAll on its parent with the appropriate selector, using :scope as you can't simply use > div or > span or > .foo.

Note that this approach ALSO selects the target element, if it matches the selector. In the below example, I'd have to use :scope > span:not(#target) to avoid selecting the entry point.

const spanSiblings = document.getElementById("target").parentNode.querySelectorAll(":scope > span");
console.log([...spanSiblings].map(e => e.innerText));
<div>
  <span>One</span>
  <span id="target">Target</span>
  <div>A</div>
  <span>Two</span>
  <div>B</div>
  <div>Hey</div>
</div>
Silviu Burcea
  • 4,689
  • 1
  • 27
  • 42
0
var childNodeArray = document.getElementById('somethingOtherThanid').childNodes;
Thunderboltz
  • 1,497
  • 3
  • 14
  • 16
-2
x1 = document.getElementById('outer')[0]
      .getElementsByTagName('ul')[1]
      .getElementsByTagName('li')[2];
x1.setAttribute("id", "buyOnlineLocationFix");
Paul Roub
  • 35,848
  • 27
  • 79
  • 88