145

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?

Bharata
  • 13,509
  • 6
  • 36
  • 50
codingbear
  • 14,773
  • 20
  • 48
  • 64

17 Answers17

180

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
  • 41,026
  • 12
  • 101
  • 131
  • 30
    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
130
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,706
  • 6
  • 36
  • 62
  • 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
30

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,593
  • 1
  • 18
  • 22
  • 3
    Nice solution. Although, I would check for nodeType to discard text node `[ ...n.parentNode.children ].filter(c => c.nodeType == 1 && c != n)` – pmrotule Jan 12 '18 at 09:08
  • How would you modify this for typescript? – Nic Scozzaro Mar 11 '20 at 14:59
  • 1
    @pmrotule [Element.children](https://developer.mozilla.org/en-US/docs/Web/API/Element/children) directly returns only the element children, so filtering by node type is redundant. – just-max Sep 21 '22 at 17:52
16

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
  • 7,713
  • 6
  • 52
  • 57
  • 1
    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
  • 5
    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
  • 703
  • 1
  • 7
  • 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,259
  • 2
  • 21
  • 20
5

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
  • 118,978
  • 58
  • 307
  • 400
Saabbir
  • 588
  • 6
  • 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
  • 11,985
  • 4
  • 38
  • 36
Gildas.Tambo
  • 22,173
  • 7
  • 50
  • 78
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
  • 3
  • 13
2

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,875
  • 4
  • 28
  • 46
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,175
  • 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
  • 5,103
  • 1
  • 29
  • 43
0

BEST SOLUTION:

let inner2 = event.target.parentNode.querySelector(`#inner2`)

/*Or if you have already stored the inner1 node to a variable called: inner1*/
let inner2 = inner1.parentNode.querySelector(`#inner2`)

At the first line the event.target will be the inner1 node, if we click on that. The parentNode will be the "outer" node, and on the partent node we start a search ( .querySelector(#inner2) ) to select the inner2 node.



OTHER SOLUTIONS:

I list other possible options, but they are not that flexible, since at them the sequence of the nodes are matter, which makes the code fragile, if we later add another node to the parent the whole code will break, what we want to avoid:

2) This selects the first child (this index starts from 1, and NOT from 0)

node.parentNode.childNodes[1]

3) Assume that you have already selected inner1Node to a variable, the next sibling you can get:

let inner2Node = inner1Node.nextElementSibling;

4) The previous sibling you can get:

let inner1NodeAGAIN = inner2Node.previousElementSibling;
Exvinp
  • 41
  • 4
0

2023 solutions

This solutions are much more shorter than in accepted answer.

ES5 solution

We can use Element.children property, then convert it to an Array and then delete the target from this array.

Element.children property gets only nodes from type Element in all modern browsers (IE9 inclusive). So filtering by node type is redundant.

For best browser compability I do not use here the function Array.from because it is not supported in IE11.

[].slice.call(HTMLCollection) – converts HTMLCollection to Array.

function getSiblings_ES5(node)
{
  var childs = [].slice.call(node.parentNode.children);
  childs.splice(childs.indexOf(node), 1);
  return childs
}

var child = document.querySelector('ul li:nth-child(3)'),
    sibls = getSiblings_ES5(child),
    i;
   
for(i in sibls)
  console.log(sibls[i].innerText);
<ul>
  <li>1</li>
  <li>2</li>
  <li style="color:red">3</li>
  <li>4</li>
</ul>

ES6 solution

var getSiblings_ES6 = node => 
    [...node.parentElement.children]
    .filter(el => el != node);
   
var child = document.querySelector('ul li:nth-child(3)'),
    sibls = getSiblings_ES6(child),
    i;
   
for(i in sibls)
  console.log(sibls[i].innerText);
<ul>
  <li>1</li>
  <li>2</li>
  <li style="color:red">3</li>
  <li>4</li>
</ul>

Documentation

Bharata
  • 13,509
  • 6
  • 36
  • 50
0
var childNodeArray = document.getElementById('somethingOtherThanid').childNodes;
Thunderboltz
  • 1,597
  • 3
  • 14
  • 16
-2
x1 = document.getElementById('outer')[0]
      .getElementsByTagName('ul')[1]
      .getElementsByTagName('li')[2];
x1.setAttribute("id", "buyOnlineLocationFix");
Paul Roub
  • 36,322
  • 27
  • 84
  • 93