I was recently working on a web page where we were using jQuery slideUp and slideDown animations, in conjunction with hide and show to reconfigure the page on-the-fly based on user interaction. We encountered a problem however in that in some cases a single user actions would result in numerous divs and table rows needing to be hidden others shown. The normal asynchronous way that animation occurs in jQuery would cause the screen to dissolve into chaos for the duration of the animations, leaving the user utterly confused as to what just happened.
To solve this problem, we wanted our animations to run either synchronously or at least sequentially. A quick google search revealed that synchronous animations are not possible because JavaScript runs on the UI thread. Thus, any synchronous animation would hang the browser for the duration. On the other hand, as anyone with any jQuery animation experience knows, it is simple to chain animations to run sequentially, using the callback argument that is part of the signature every animation method.
Our problem was that we needed a way to do sequential animations on sets of elements. For example, a call like $(“.widgets”).slideDown() causes all widgets to slide down concurrently. We wanted them to slide down one after another, so that the user can better track with their eye what is happening.
Moreover, some additional logic in the handling of the same user event (say a button click) would cause all nuggets to slide up and the #fooBar element slide down as well. We wanted all this to happen sequentially without the disparate sets of logic that made these determinations needing to know of or explicitly coordinate with each other.
To solve this problem I wrote the following simple pair of JavaScript functions that work together to 1) register animations to run sequentially and then 2) kicks off the next animation in the queue once the previous one completes.
var animationFIFOQueue = new Array(); function doAnimationSequentially(action, domObj) { var animation = { action: action, domObj: domObj }; var needToStartAnimationChain = animationFIFOQueue.length == 0; animationFIFOQueue.push(animation); if (needToStartAnimationChain) { doNextAnimation(); } } function doNextAnimation() { var nextAnimation = animationFIFOQueue.shift(); if (nextAnimation) { if (nextAnimation.action == "slideDown") { $(nextAnimation.domObj).slideDown(doNextAnimation); } if (nextAnimation.action == "slideUp") { $(nextAnimation.domObj).slideUp(doNextAnimation); } if (nextAnimation.action == "hide") { $(nextAnimation.domObj).hide(); doNextAnimation(); } if (nextAnimation.action == "show") { $(nextAnimation.domObj).show(); doNextAnimation(); } } } |
Note that the code also allows for the insertion of non-animated (and hence synchronous) hide and show commands into the animation sequence. We used this for hiding and showing table rows elements in coordination with sliding the content of the rows since rows themselves to not animate well.
Obviously the code can easily be expanded to handle other animation commands as well (such as fadeIn and fadeOut.)