Reputation: 98
I have this markup:
<h3 id="btn-1">Content Title</h3>
<ul><li>Some Content</li></ul>
...stuff here and further down the page...
<h3 id="btn-2">Content Title 2</h3>
<ul><li>Some content 22</li></ul>
The content in the unordered list is supposed to drop down when "h3" is clicked and also append a class to h3 so as to create a style based on the open/close state. So I have this which is working as I want:
$("#btn-1, #btn-2").attr('class','inactive');
$("#btn-1").click(function (){
if ($("#btn-1 + ul").is(":hidden")) {
$("#btn-1 + ul").slideDown("slow"),$("#btn-1").attr('class','active');
} else {
$("#btn-1 + ul").slideUp('slow'),$("#btn-1").attr('class','inactive');
}
});
$("#btn-2").click(function (){
if ($("#btn-2 + ul").is(":hidden")) {
$("#btn-2 + ul").slideDown("slow"),$("#btn-2").attr('class','active');
} else {
$("#btn-2 + ul").slideUp('slow'),$("#btn-2").attr('class','inactive');
}
});
I was looking for a more efficient way to write the code and I came up with this but it applies the animation to all the containers at the same time.
$("h3[id^='btn-']").attr('class','inactive');
$("h3[id^='btn-']").click(function (index){
if ($("h3[id^='btn-'] + ul").is(":hidden")) {
$("h3[id^='btn-'] + ul").slideDown("slow"),$("h3[id^='btn-']").attr('class','active');
} else {
$("h3[id^='btn-'] + ul").slideUp('slow'),$("h3[id^='btn-']").attr('class','inactive');
}
});
So my question is, is there a better way to reduce the code in the first working solution? Thanks.
Upvotes: 2
Views: 258
Reputation: 5090
While not that readable, you asked for efficient, here it is in one line:
//hide content then only slide up other visible things
$("h3[id^='btn-']").next("ul").hide().end().click(function (){ $("h3[id^='btn-']").next("ul").not( $(this).next("ul").slideDown("slow").end().next("ul")).slideUp("slow"); });
And a more readable formatting:
//hide content then only slide up other visible things
$("h3[id^='btn-']").next("ul").hide().end().click(function() {
$("h3[id^='btn-']").next("ul").not(
$(this).next("ul").slideDown("slow").end().next("ul")).slideUp("slow");
});
Try it: http://jsfiddle.net/t4JF4/
Upvotes: 0
Reputation: 75993
Use this
inside your callback function to refer to the element which is having the event fire on it:
$("h3[id^='btn-']").attr('class','inactive').click(function (index){
$(this).toggleClass('inactive active').next().slideToggle("slow");
});
You can see that this simplifies your code even further by using toggle functions:
.toggleClass()
: http://api.jquery.com/toggleClass.slideToggle()
: http://api.jquery.com/slideToggleHere is a demo: http://jsfiddle.net/jasper/a692n/3/
The RegExp selector you're using works but it has to look-up every element in the DOM to see if that element has the specified attribute and if that attribute value matches. I recommend adding a class to the <h3>
elements because when you select by class jQuery uses getElementByClass
where it's available (which is a fast way to select elements):
HTML --
<h3 class="h3-title" id="btn-1">Content Title</h3>
<ul><li>Some Content</li></ul>
...stuff here and further down the page...
<h3 class="h3-title" id="btn-2">Content Title 2</h3>
<ul><li>Some content 22</li></ul>
JS --
$(".h3-title").attr('class','inactive').click(function (index){
$(this).toggleClass('inactive active').next().slideToggle("slow");
});
Upvotes: 3
Reputation: 227190
Inside your click handler, you can use this
to get the element clicked on (instead of using $("h3[id^='btn-']")
, this gets all the <h3>
s), and then get the <ul>
next to it.
$("h3[id^='btn-']").attr('class','inactive');
$("h3[id^='btn-']").click(function (index){
if ($(this).next('ul').is(":hidden")) {
$(this).next('ul').slideDown("slow");
$(this).attr('class','active');
}
else {
$(this).next('ul').slideUp('slow');
$(this).attr('class','inactive');
}
});
DEMO: http://jsfiddle.net/8cFeF/1/
Upvotes: 3