0

I know my problem I just not sure how to resolve it. I have a custom domain and in a function call a while loop executes. In that loop i wanted an animation to occur in order.

So the first problem is that javascript by its nature executes every line thus item 2 starts before item 1 completes. Now the effect is so short that it "appears" to happen to all elements at once but in the debugger it is just looping one at a time.

Now my typical resolution would be to use SetTimeout() but that is causing the browser to lock. Reading this post (Trying to delay/pause/slow a while loop in jQuery) it makes sense that the browser is getting into an endless loop.

So how can I get a pause between element1 and element2 events? I thought perhaps to add a callback function to my custom domain but not sure if that will work as desired besides not being sure how to do it.

In the head of the page and read the comments for anything else I may be doing wrong or could do better.

$(document).ready(function ()
{
    //pause long enough for person to visually take in page before starting
    setTimeout(function () { PageLoadAnimation.onReady(); }, 1000);
});

My custom domain:

var PageLoadAnimation = 
{
    onReady: function ()
    {
        //black everything out just to be sure
        PageLoadAnimation.BlackOutElements();

        //flash & show
        PageLoadAnimation.FlashElement();

    },

    BlackOutElements: function ()
    {
        $('#ParentContainer').children().hide();
    },

    FlashElement: function ()
    {
        //get array of all elements and loop till all are visible
        var elementArray = $('#ParentContainer').children();

        var $els = $('#PartialsContainer').children();

        while (elementArray.length)
        {
           var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));

           //if I put set timeout here is causes the infinite loop
           PageLoadAnimation.FlashBlast($el);

           elementArray = elementArray.not($el);

           //if I put by itself it no diff as the while loop continues regardless
           //setTimeout(1500);


        }
    },

    FlashBlast: function ($el)
    {
       //flash background
       $el.fadeIn(200, function () { $el.fadeOut(200) });
    }

}

I'm not sure if it isn't working or if I am doing something wrong so I created these fiddles:

Original Fiddle

With Johan Callbacks

Using is animating property WARNING THIS ONE WILL HANG YOUR BROWSER! I don't think I am checking the isAnimating property the way Johan had in mind??


ANSWER FOR THIS SITUATION. Hopefully it will help others.

setTimeout in a loop was really my problem...but not the only problem. I was the other problem(s).

Me first.

Fool that I am I was really causing my own complications with two things I was doing wrong.

First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.

Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.

WRONG: intervalTimer = setInterval(MyFunction(), 1500);

RIGHT: intervalTimer = setInterval(MyFunction, 1500);

As for the code I read here (http://javascript.info/tutorial/settimeout-setinterval) setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.

Here is my implementation:

I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.

var PageLoadAnimation =
               {
                   elementArray: null,
                   intervalTimer: null,
                   ....
                }

In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.

onReady: function () 
      {
         elementArray = $('#PartialsContainer').children();

         //black everything out just to be sure
         PageLoadAnimation.BlackOutElements();

         //flash & show
         intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);

       },

Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.

FlashElement: function () 
{

   if(elementArray.length > 0) //check how many elements left to be flashed
   {
      var $el = PageLoadAnimation.GrabElement(); //get random element
      PageLoadAnimation.FlashBlast($el); //flash it
      PageLoadAnimation.RemoveElement($el); //remove that element
   }
   else
   {
      //done clear timer
      clearInterval(intervalTimer);
      intervalTimer = null;
   }


},

So the whole thing is:

var PageLoadAnimation =
               {
                   elementArray: null,
                   intervalTimer: null,

                   onReady: function () {
                       elementArray = $('#PartialsContainer').children();

                       //black everything out just to be sure
                       PageLoadAnimation.BlackOutElements();

                       //flash & show
                       intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
                       //NOT this PageLoadAnimation.FlashElement()

                   },

                   BlackOutElements: function () {
                       $('#PartialsContainer').children().hide();
                   },

                   FlashElement: function () 
                   {

                       if(elementArray.length > 0)
                       {
                           var $el = PageLoadAnimation.GrabElement();
                           PageLoadAnimation.FlashBlast($el);
                           PageLoadAnimation.RemoveElement($el);
                       }
                       else
                       {
                           //done clear timer
                           clearInterval(intervalTimer);
                           intervalTimer = null;
                       }


                   },

                   GrabElement: function()
                   {
                       return elementArray.eq(Math.floor(Math.random() * elementArray.length));
                   },

                   RemoveElement: function($el)
                   { elementArray = elementArray.not($el); },

                   FlashBlast: function ($el) {
                       //flash background
                      $el.fadeIn(100, function () { $el.fadeOut(100) });
                   }



               }

Hope that help others understand the way to go about pausing execution in javascript.

Community
  • 1
  • 1
GPGVM
  • 5,515
  • 10
  • 56
  • 97
  • Well the idea is that I want the effect to happen to all elements but at random. So I select an element at random then pass that element to the FlashBlast to perform the effect. However javascript continues to the next line removing it from the array and selecting another elelemnt passing it to FlashBlast...etc So that is where I think that perhaps FlashBlast should have a callback so the next line doesn't execute till complete. – GPGVM Dec 12 '13 at 14:15
  • Sorry was afk for a while. I will provide ou with a callback example, maybe it can help. – Johan Dec 12 '13 at 15:59

1 Answers1

1

A callback example that might help:

FlashBlast: function ($el, fadeInComplete, fadeOutComplete)
{
   if(arguments.length === 3){

       $el.fadeIn(200, function () {
           fadeInComplete();
           $el.fadeOut(200, fadeOutComplete);
       });
   }

}

Usage:

PageLoadAnimation.FlashBlast($el, function(){
    //fadein complete
}, function(){
    //fadeout complete
});

Another idea that might help:

 isAnimating: false,

 FlashBlast: function ($el)
 {
     var dfd = $.Deferred(),
         that = this;

     that.isAnimating = true;

     $el.fadeIn(200, function () { 
         $el.fadeOut(200, function(){
             dfd.resolve();
         }) 
     });

     dfd.done(function(){
         that.isAnimating = false;
     });
 }

Then make use of the private property isAnimating.

Finally, to know if an element is under an animation, you can use $el.is(':animated').

Hope this helps. Let me know if anything is unclear.

Johan
  • 35,120
  • 54
  • 178
  • 293
  • 1
    So a callback for the fadein and one for the fade out. Very cool. You taught me how to do a callback as well as an expanded way of thinking / using them! – GPGVM Dec 12 '13 at 16:24
  • 1
    @user1278561 It's actually a good example, since it shows both how to call them and pass them as a function reference to somehting else (`fadeOut` in this case). Glad it helped. Not sure if I answered the question though, but hey. – Johan Dec 12 '13 at 16:43
  • 1
    I'm exploring your solution now but have to go to a mtg for ~hour so it'll be later today I provide feedback. – GPGVM Dec 12 '13 at 16:46