Reputation: 699
I have some collapsible panel divs that have a title attribute. When my jQuery opens the panel, I want the title attribute to change and I want to specify the div to change via the class of the panel currently being opened. i.e. this.div.class change title to "whatever".
To make the code stupid simple for your to follow:
<div class="panelContainer">
<div class="service">
<div class="serviceBrief" title="Click to Read More">
<p>Some Stuff for closed panel</p>
</div> <!-- end serviceBrief -->
<div class="serviceDescContainer">
<div class="serviceDesc">
<p>some more stuff that shows when panel is open</p>
</div><!-- end serviceDesc -->
</div><!-- end serviceDescContainer -->
</div><!-- end service -->
<div class="service">
<div class="serviceBrief" title="Click to Read More">
<p>Some Stuff for closed panel</p>
</div> <!-- end serviceBrief -->
<div class="serviceDescContainer">
<div class="serviceDesc">
<p>some more stuff that shows when panel is open</p>
</div><!-- end serviceDesc -->
</div><!-- end serviceDesc Container -->
</div><!-- end service -->
</div> <!-- end panelContainer -->
I understand how to do this using ID's
$('#sampleID').attr('title', 'Click to Read More');
But I want to do this referencing the div class to change the title attribute so when the panel is open the title="Click to Read Less"
I thought this would work:
$('.serviceBrief').attr('title', 'Click to Read Less');
and it does, but obviously it changes all instances of the title attribute instead of just the one that is open. I know I am missing making this a "this" type command in jQuery, but all my various attempts are failing and I can't for the life of me find a reference anywhere.
Can someone point me in the right direction? Thanks!
$(document).ready(function() {
$('.serviceBrief').each(function(){
$(this).append('<div class="panelOpenArrow"></div><div class="panelClosedArrow"></div>');
});
$('.serviceBrief').click(function(){
if ($(this).parent().is('.open')) {
$(this).closest('.service').find('.serviceDescContainer').animate({'height':'0'},500);
$(this).closest('.service').find('.panelOpenArrow').fadeOut(500);
$(this).closest('.service').find('.panelClosedArrow').animate({'height': '25px'});
$(this).closest('.service').removeClass('open');
}else{
var newHeight = $(this).closest('.service').find('.serviceDesc').height() + 'px';
$(this).closest('.service').find('.serviceDescContainer').animate({'height':newHeight},500);
$(this).closest('.service').find('.panelOpenArrow').fadeIn(500);
$(this).closest('.service').find('.panelClosedArrow').animate({'height':'0'});
$(this).closest('.service').addClass('open');
}
});
});
Upvotes: 0
Views: 8357
Reputation:
You could write this all much easier. I'll shorten it "some", meaning there is even more beyond what I will show, but hopefully this breakdown will help you understand how powerful jQuery can really be.
<script type="text/javascript">
$(function() { // same as $(document).ready ... but SHORTER!
$('.serviceBrief').each(function(i) { // of course i stands for the 0 based index of the elements in this object
$(this).append( // many different ways to (pre|ap)pend elemnts, this is my fav do to the "readability"
$('<div />', { class: 'panelArrow panelOpenArrow' }), // set attributes in the { }
$('<div />', { class: 'panelArrow panelClosedArrow' }) // don't forget to place comma before appending more elements
// keep in mind, you could continue inside here and append to what is being appended!
)
}) // here I continue to "chain", no need to recall the same object
.click(function(e) { // simple click event, you might also look at "delegate"ing events
var aroOpen = $(this).children('.panelOpenArrow'),
aroClose = $(this).children('.panelClosedArrow');
// i establish these variable for ease of use in next event
// considering the way your HTML is layed, there's really no need for all that "find"ing, it's just more code time, less action time!
$(this).next('.serviceDescContainer').slideToggle(500, function(e) { // this is much the same as what you were trying to do using .animate
if ($(this).is(':visible')) { // kind of like your class check, except this checks the display, opacity, and even considers height (in newer jQuery versions) for "visibility"
// at this point, this first line is "unneccesary", but I left it here in case you were doing some "CSS" using that class name
$(this).closest('.service').addClass('open');
// .stop prevents animations previously taking place, like if a user clicks this real fast
aroOpen.stop().fadeOut(500);
aroClose.stop().animate({ height: '25px' });
}
else {
$(this).closest('.service').removeClass('open');
aroOpen.stop().fadeIn(500);
aroClose.stop().animate({ height: 0 });
}
})
});
})
</script>
.delegate
in jQuery 1.7+ with .on()!<script type="text/javascript">
$(function() {
$('.serviceBrief').each(function(i) {
$(this).append(
$('<div />', { class: 'panelArrow panelOpenArrow' }),
$('<div />', { class: 'panelArrow panelClosedArrow' })
)
})
.click(function(e) {
var aroOpen = $(this).children('.panelOpenArrow'),
aroClose = $(this).children('.panelClosedArrow');
$(this).next('.serviceDescContainer').slideToggle(500, function(e) {
if ($(this).is(':visible')) {
$(this).closest('.service').addClass('open');
aroOpen.stop().fadeOut(500);
aroClose.stop().animate({ height: '16px' });
}
else {
$(this).closest('.service').removeClass('open');
aroOpen.stop().fadeIn(500);
aroClose.stop().animate({ height: 0 });
}
})
});
})
</script>
Upvotes: 1
Reputation: 663
You can pass your handler function a parameter e
containing the event that triggered it. The e.currentTarget
property will contain the actual element that is handling the event, so you can change the attribute of that to only affect the current element.
$('.serviceBrief').click(function(e){
var objThis = $(e.currentTarget);
var objService = objThis.parent();
if (objService.is('.open')) {
objService.find('.serviceDescContainer').animate({'height':'0'},500);
objService.find('.panelOpenArrow').fadeOut(500);
objService.find('.panelClosedArrow').animate({'height': '25px'});
objService.removeClass('open');
objThis.attr("title", "Click to Read More");
}else{
var newHeight = objService.find('.serviceDesc').height() + 'px';
objService.find('.serviceDescContainer').animate({'height':newHeight},500);
objService.find('.panelOpenArrow').fadeIn(500);
objService.find('.panelClosedArrow').animate({'height':'0'});
objService.addClass('open');
objThis.attr("title", "Click to Read Less");
}
});
Here is a fiddle: http://jsfiddle.net/gtXpx/
It's a good idea to cache your DOM queries in objects to improve performance.
Upvotes: 1
Reputation: 13344
You're right on the money. Just reference $(this)
in your click event to apply the attribute to the clicked element, and not all .serviceBrief
elements:
$('.serviceBrief').click(function(){
if ($(this).parent().is('.open')) {
$(this).attr( "title", "Click to Read Less");
$(this).closest('.service').find('.serviceDescContainer').animate({'height':'0'},500);
$(this).closest('.service').find('.panelOpenArrow').fadeOut(500);
$(this).closest('.service').find('.panelClosedArrow').animate({'height': '25px'});
$(this).closest('.service').removeClass('open');
}else{
var newHeight = $(this).closest('.service').find('.serviceDesc').height() + 'px';
$(this).attr( "title", "Click to Read More");
$(this).closest('.service').find('.serviceDescContainer').animate({'height':newHeight},500);
$(this).closest('.service').find('.panelOpenArrow').fadeIn(500);
$(this).closest('.service').find('.panelClosedArrow').animate({'height':'0'});
$(this).closest('.service').addClass('open');
}
});
Upvotes: 1
Reputation: 104775
Why not just do:
$('.serviceBrief').click(function(){
if ($(this).parent().is('.open')) {
$(this).attr('title', 'Click to Read Less');
//rest of your code
Upvotes: 1