Attach a close event to a div that already has fadeOut attached to it
I implemented the "growl" popup example from the book "jquery: novice to ninja", basically it shows a growl style popup in your browser which can be closed by clicking on a "close" link in it. The code works great but I want to improve it by having the popup fadeOut after some time IF the user never clicked on it to close it (it can get rather annoying to have to keep closing them). The original code looks like this:
I have this simple div in my HTML and styled it accordingly as per the example in the book
<div id="message_box">
</div>
and here is the jquery code (again straight from the book):
/**
* function that adds notices to growl like message box in bottom right corner
*/
function addNotice(notice) {
$('<div class="notice"></div>')
.append('<div class="skin"></div>')
.append('<a href="#" class="close">close</a>')
.append($('<div class="content"></div>').html($(notice)))
.hide()
.appendTo('#message_box')
.fadeIn(1000)
}
/**
* event handler for closing growl like message box
*/
$('#message_box')
.find('.close')
.live('click', function() {
$(this)
.closest('.notice')
.animate({
border: 'none',
height: 0,
marginBottom: 0,
marginTop: '-6px',
opacity: 0,
paddingBottom: 0,
paddingTop: 0,
queue: false
}, 1000, function() {
$(this).remove();
});
});
/* Give it a whirl */
$(document).ready(function() {
addNotice("<p>Welcome</p>");
});
I simply modified the addNotice function like this, adding the fadeOut with a delay (I also tried without the delay, same issue):
function addNotice(notice) {
$('<div class="notice"></div>')
.append('<div class="skin"></div>')
.append('<a href="#" class="close">close</a>')
.append($('<div class="content"></div>').html($(notice)))
.hide()
.appendTo('#message_box')
.fadeIn(1000)
/* This works but seems to disable the close functional开发者_开发问答ity */
.delay(10000).fadeOut(1000, function(){$(this).remove();});
} .delay(10000).fadeOut(1000, function(){$(this).remove();});
but when I do this, the close event is not working anymore; when you click on the close link, the box isn't closing. It will fade out after the specified time, but I would like to have both options available to the users (close immediately or just have it fade).
Can somebody please tell me what I am doing wrong? Why is the close event never triggered/caught?
Cheers, Mark.
You need to add .clearQueue().stop()
to the close action. Animations are executed in order, sequentially, and not in parallel. Because you queue a fadeIn
, and immediately a fadeOut
, both of those will execute before your close animate()
call runs. stop()
will stop the current animation (the delay or the fadeout), clearQueue will empty the animation queue so whatever animate
you call next will run immediately :
$('#message_box')
.find('.close')
.live('click', function() {
$(this)
.closest('.notice')
.clearQueue()
.stop()
.animate({
border: 'none',
height: 0,
marginBottom: 0,
marginTop: '-6px',
opacity: 0,
paddingBottom: 0,
paddingTop: 0,
queue: false
}, 1000, function() {
$(this).remove();
});
});
From the documentation on delay()
:
Set a timer to delay execution of subsequent items in the queue
In other words, delay()
stops the execution of any animations (such as sliding, fading and other animations) for the given amount of time. After the duration specified has passed, it resumes the animations.
What happens in your original case is this:
- Your notice shows
- You click on close
- Notice is animated and fades out
What happens when you add the delay code:
- Your notice shows.
- It then adds a delay on 10 seconds. This will prevent any animations from running for the next 10 seconds
- It adds
fadeOut(1000, function(){$(this).remove();});
to the queue of animations (this will be the next animation to be run after the 10 seconds of delay is up) - You click on close.
- The closing animation is queued as number 2 in the queue, after the first
fadeOut
you added previously. In other words, it is not run, since you still have set a delay - The delay of 10 seconds passes
- Your first animation,
fadeOut(1000, function(){$(this).remove();});
, is run - removing the notice - Your second animation (the one fired by clicking on close) is run. Since you've already removed the notice in the last animation, it has no use
The solution. Use
setTimeout
to set a timeout for the closing animation if you haven't clicked on the close link before its timeout delay has passed See accepted answer. Leaving my answer for the history books
function addNotice(notice) {
var notice =
$('<div class="notice"></div>')
.append('<div class="skin"></div>')
.append('<a href="#" class="close">close</a>')
.append($('<div class="content"></div>').html($(notice)))
.hide()
.appendTo('#message_box')
.fadeIn(1000)
/* Close the timeout after 10 seconds */
setTimeout(function() {
notice.fadeOut(1000, function(){$(this).remove();});
}, 10000);
}
Edit: Mike's answer was more elegant in this case. I don't really like using setTimeout
, though if you were to use other animations on the notice, using delay could make some problems.
精彩评论