Siblings
Elements that share the same parent are siblings. Moving between them is how you build tabs, accordions, step-by-step forms and "one item selected at a time" lists.
<ul>
<li>Home</li>
<li id="shop">Shop</li>
<li>Blog</li>
<li class="special">Offers</li>
<li>Contact</li>
</ul>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const $shop = $("#shop");
console.log("siblings ", $shop.siblings().length, $shop.siblings().text());
console.log("next ", $shop.next().text());
console.log("prev ", $shop.prev().text());
console.log("nextAll ", $shop.nextAll().text());
console.log("prevAll ", $shop.prevAll().text());
console.log("nextUntil ", $shop.nextUntil(".special").text());
console.log("index ", $shop.index());
</script>siblings 4 HomeBlogOffersContact
next Blog
prev Home
nextAll BlogOffersContact
prevAll Home
nextUntil Blog
index 1.siblings()returns every other child of the same parent. The element itself is never included..next()and.prev()return the one neighbour directly after or before..nextAll()and.prevAll()return all siblings in that direction..nextUntil(".special")collects siblings up to, but not including, the first match..prevUntil()works the other way..index()tells you the element's position among its siblings, counting from 0.
A selector on .next() filters, it does not search
This catches many people:
<h3>Question</h3>
<p class="note">A note between them.</p>
<p class="answer">The answer.</p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
console.log(".next('.answer') :", $("h3").next(".answer").length);
console.log(".nextAll('.answer') :", $("h3").nextAll(".answer").first().text());
</script>.next('.answer') : 0
.nextAll('.answer') : The answer..next(".answer") looks only at the immediate next sibling (the note) and keeps it if it matches. It does not, so the result is empty. To find the first following .answer, use .nextAll(".answer").first().
One selected at a time
The most common sibling pattern: highlight the item that was clicked and remove the highlight from the others.
<style>
li { cursor: pointer; padding: 4px 8px; }
li.active { background: #fde68a; font-weight: bold; }
</style>
<ul>
<li class="active">Small</li>
<li>Medium</li>
<li>Large</li>
</ul>
<p>Selected size: <span id="size">Small</span></p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("li").on("click", function () {
$(this).addClass("active").siblings().removeClass("active");
$("#size").text($(this).text());
console.log("selected index", $(this).index());
});
</script>selected index 2One chained line does the work: add active to the clicked item, move to its siblings, remove active from them.
A simple accordion
.next() connects a heading to the panel that follows it:
<style>
.panel { display: none; padding: 0 12px; }
.open + .panel { display: block; }
h4 { cursor: pointer; margin: 8px 0; }
</style>
<h4 id="q1">What is jQuery?</h4>
<div class="panel">A JavaScript library.</div>
<h4 id="q2">Is it free?</h4>
<div class="panel">Yes, it is open source under the MIT licence.</div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("h4").on("click", function () {
$(this).toggleClass("open");
console.log($(this).next(".panel").text());
});
</script>Yes, it is open source under the MIT licence.The CSS shows a panel when the heading before it has the open class, and jQuery only toggles that class. Keeping the look in CSS and the behaviour in jQuery is a clean split; the effects section shows animated versions.
Tabs with .index() and .eq()
.index() and .eq() work well together: find the position of the clicked tab, then show the panel at the same position.
<style>
.tab.on { font-weight: bold; text-decoration: underline; }
.pane { display: none; } .pane.on { display: block; }
</style>
<div id="tabs"><button class="tab on">Description</button><button class="tab">Reviews</button><button class="tab">Shipping</button></div>
<div class="pane on">A solid oak desk.</div>
<div class="pane">4.5 stars from 30 reviews.</div>
<div class="pane">Ships in 2 days.</div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$(".tab").on("click", function () {
const i = $(this).index();
$(this).addClass("on").siblings().removeClass("on");
$(".pane").removeClass("on").eq(i).addClass("on");
});
</script>DescriptionReviewsShipping
4.5 stars from 30 reviews.After clicking the second tab, only the second panel is visible.
Notice that .index() is measured among the tab's own siblings, the buttons inside #tabs, not among every element on the page. That is why the tabs sit in their own container. If you add an unrelated element inside #tabs, such as a heading before the first button, every index shifts by one and the wrong panel opens. To protect against that, call .index() with a selection: $(".tab").index(this) returns the position of the clicked tab among the .tab elements only, whatever else shares the container.
Common mistakes
- Expecting
.siblings()to include the element. It never does. - Using
.next(selector)to search ahead. Use.nextAll(selector).first(). - Forgetting that whitespace and comments are skipped. jQuery's sibling methods only count elements, so line breaks in your HTML do not matter.
- Linking tabs and panels by position when the HTML order can change. For larger widgets, use
data-attributes oraria-controlsto connect them by name.
Exercise
Build a list of three colour names. When one is clicked, make it bold and make all the other items gray, using .siblings().
Show one possible answer
<ul><li>Red</li><li>Green</li><li>Blue</li></ul>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("li").on("click", function () {
$(this).css({ fontWeight: "bold", color: "" })
.siblings().css({ fontWeight: "", color: "gray" });
});
</script>Setting a style to an empty string removes it, so an item that was gray goes back to normal when clicked.
Key points
- Siblings share the same parent; .siblings() returns all of them except the element itself.
- .next() and .prev() return the single neighbour after or before; .nextAll() and .prevAll() return every one in that direction.
- .nextUntil(selector) and .prevUntil(selector) stop just before a matching element.
- .index() returns an element's position among its siblings, counting from 0.