Using SetTimeout() To Delay The Closing Of A Related UI Element Based On User Interactions
- setTimeout( function, interval )
- setInterval( function, interval )
NOTE: There are actually other ways to invoke these functions; but, using a function reference is the only way that I would recommend using them.
Each of these functions will invoke the given callback (function argument) after the given interval has passed. The difference between these two functions is that setTimeout() will only invoke the callback once whereas setInterval() will invoke the given callback over and over again, pausing between invokations for a duration defined by the given interval.
When you call these functions - setTimeout() and setInterval() - they pass back a unique token that represents the timer that you have just configured:
var timerID = setTimeout( function, interval ); var timerID = setInterval( function, interval );
There is no requirement to store this token; however, storing this token will allow you to cancel the future invocation of your callbacks, should the conditions of your application change. Once you have these timer tokens, you can cancel the timers using either the clearTimeout() or clearInterval() function:
- clearTimeout( timerID )
- clearInterval( timerID )
As you might have guessed, clearTimeout() can be used in conjunction with the setTimeout() tokens and clearInterval() can be used in conjunction with the setInterval() tokens. Once a timer has been cleared, it will not fire again. Should you want to re-configure a timer, you will need to call either the setTimeout() or setInterval() functions again.
Now that we see how to plan the future execution of callbacks, let's see how these functions can be used to delay UI reactions based on user interactions. In the following demo, we have an external link that triggers the opening of a footer area. If the user does not interact with the footer in a given amount of time (4 seconds), the footer will close automatically.
In this demo, the user can actively engage in three different interactions:
- Open the footer.
- Mouse into the footer.
- Mouse out of the footer.
When the user clicks the open-footer link, we want to show the footer and configure a timer that will close the footer if the user chooses not to engage with it. We do this through the use of setTimeout(). In the above code, if the user does not engage with the footer within 4,000 milliseconds (4 seconds), our timer will execute the closeFooter() function, sliding the footer down and out of view.
If the user does choose to engage with the footer, however, we want to make sure that the footer does not close while the user is interacting with it. As such, when the user mouses into (mouseenter) the footer, we use clearTimeout() to cancel the future invocation of our closeFooter() callback.
In this particular demo, I am checking the animation status of the footer before I perform any further UI logic. In this approach, we don't have to worry about double-clicking and mid-animation interactions. We can always expand our logic to deal with such situations later; however, that quickly adds complexity that is not relevant for this explanation.
While approaches to this kind of situation may vary widely, I think you'll find that they typically operate off of the setTimeout() and clearTimeout() functions. I hope that this helps shed some light on delayed interface reactions based on user interactions.
Want to use code from this post? Check out the license.
Ben, take a look at my jQuery doTimeout plugin, which is very useful for delayed code execution, especially when dealing with polling loops or hover intent:
In addition, my jQuery throttle / debounce plugin is geared towards rate-limiting function execution, so you might find that useful when dealing with other events:
FYI - I've been meaning to mention this for a while, but you should consider increasing the volume on your mic. I always end up having to jack up my volume to hear any of your screencasts. The volume on your screencasts is easily 70%+ quieter than other sources I listen to. I have Winamp at 20% volume and it's still way louder than your screencasts. In order to hear you, I always have to turn off any other audio and jack the volume way up.
<stron>Wow Ben - I did not expect such a amount of work just to make happen what I had in mind but it is exactly what I was looking for. Very well done as always. Thank you very, very much for spending your time on that. I think I can use that very often.
Very cool plugins - you're like a jQuery plugin wizard. I remember at last year's jQuery conference you told me you had a lot of ideas for plugins... no doubt! I particularly the like throttle() concept.
I agree with you on that 100%. But, I'll be straight-up honest - I have no idea how to do that. Would that be a system setting, or an app setting (JING) do you think?
Also, I seem to remember you also writing some "debouncing" code a good while ago.
Happy to help. Take a look at Ben Alman's jQuery plugins listed earlier in the comments; now that you are more familiar with the concept, his plugins should simplify your life a bit.
Yes, I have seen that plugin but you know what. All those plugins make you forget (or never tell you) how it all actually works. So I like the plugins in general since they speed up our work but I also like a good Tutorial very much. That is what makes me smart at the end. All other is just convenient. Looking forward to your next Tuto. Have a good one...
I completely agree - sometimes it's great to pick things apart or go back to the basics to remember how they work in the first place.
I have used setTimeout() many a time, and never knew it returned a token or that you could use that to clear it! Awesome! All this time, I would have a global variable that I would set that would flag the scheduled function whether it should proceed or abort (wrap it's actions on a big if block).
This is much cleaner.
I used to go the "flag" route as well; having a way to programmatically clear a timer will simplify your life greatly.
Ben....today I feel very "blocked" and maybe you can give me a hint to make it work. I use your script but I have 2 openFooterLink trigger on my site for opening the footer panel. 1 should work on mouseenter the other one on click. I just can not make it work. Can you point me? Cheers to NYC from rainy Berlin.
@Ben - I figured out on my own. It was some kind of strange. I was on the right track with creating another var for the second trigger and setting up a click function for it but it was not working because the trigger was wrapped in a <a> tag (as I found out later). It always worked with mouseenter but not with the click event. I then wrapped my trigger in a <span> tag and it all worked like a charme.
Glad you got it working. I am not sure what an A vs. SPAN would do; but, glad you got it working regardless :)