In JavaScript, siblings are nodes that share the same parent. Navigating between them is a common task when you need to change the state of adjacent elements, such as opening an accordion or highlighting a neighboring menu item.
The DOM provides two sets of properties for sibling navigation: one for all nodes (including text and comments) and one specifically for elements (HTML tags).
1. Element-Only Sibling Navigation
For 99% of web development tasks, you will want the element properties. These ignore the “whitespace” (text nodes) created by line breaks and indentation in your HTML code.
nextElementSibling
Returns the next element at the same tree level. If there is no subsequent element, it returns null.
JavaScript
const current = document.querySelector('.active');
const next = current.nextElementSibling;
if (next) {
next.classList.add('highlight');
}
previousElementSibling
Returns the preceding element at the same tree level. Returns null if the element is the first child.
JavaScript
const prev = current.previousElementSibling;
2. All-Node Sibling Navigation
These properties are more “low-level.” They return the absolute next or previous node, which is often a Text node (whitespace/newline) rather than an actual HTML tag.
- nextSibling: Returns the next node of any type.
- previousSibling: Returns the previous node of any type.
[!TIP] Why avoid these? In the following HTML:<div><p>A</p> <p>B</p></div>ThenextSiblingof<p>A</p>is actually the space between the two tags, not<p>B</p>. Always prefer the “Element” versions unless you are specifically manipulating text nodes.
3. Selecting All Siblings
There is no native getSiblings() method. To find all siblings of an element, you must navigate to the parent, get all its children, and then filter out the original element.
JavaScript
const getSiblings = (el) => {
// 1. Get the parent
const parent = el.parentElement;
// 2. Convert parent's children to an array and filter
return Array.from(parent.children).filter(sibling => sibling !== el);
};
const otherItems = getSiblings(document.querySelector('.selected'));
otherItems.forEach(s => s.style.opacity = '0.5');
4. Practical Use Case: Accordion Logic
Sibling navigation is essential for “exclusive” UI elements, where opening one item should close its neighbors.
JavaScript
const headers = document.querySelectorAll('.accordion-header');
headers.forEach(header => {
header.addEventListener('click', () => {
// Find the next sibling (the content panel)
const panel = header.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
});
5. Advanced Patterns
The “Loop-Around” Sibling
Sometimes you need to create a carousel or menu where clicking “next” on the last item takes you back to the first. You can combine sibling properties with parent properties to achieve this:
function getNextOrFirst(el) {
return el.nextElementSibling || el.parentElement.firstElementChild;
}
Dealing with nodeType
If you are forced to use nextSibling but only want elements, you can check the nodeType.
- Element Node: nodeType === 1
- Text Node: nodeType === 3
JavaScript
let node = el.nextSibling;
while (node && node.nodeType !== 1) {
node = node.nextSibling;
}
// 'node' is now the next Element node, mimicking nextElementSibling
6. Performance and Best Practices
The Chaining Risk
You can chain sibling properties (e.g., el.nextElementSibling.nextElementSibling), but this is highly “brittle.” If your HTML structure changes slightly (like adding a decorative <span>), your script will break.
Better Approach: Use parentElement.querySelector() or closest() to find a specific neighbor by class or ID rather than relying on strict positional order.
Using Iterators
If you need to find all siblings that match a specific condition after the current element, you can use a while loop:
let sibling = element.nextElementSibling;
while (sibling) {
if (sibling.matches('.important')) {
console.log('Found important sibling:', sibling);
}
sibling = sibling.nextElementSibling; // Move to the next one
}
