views:

275

answers:

8

I need to flash an element off and on. This works but I don't really like the code. Is there a nice way of doing this?

setTimeout(function(){
  toggle();
  setTimeout(function(){
    toggle();
    setTimeout(function(){
      toggle();
      setTimeout(function(){
        toggle();
      }, 100);
    }, 100);
  }, 100);
}, 100);

I'm using jQuery too if that helps.

+1  A: 

I would use a blinking effect. For jquery there's pulsate, hope that works for you.

toby
+4  A: 
function toggle_multiple(n)
{
    var toggled = 0;
    function toggle_one_time()
    {
        toggle();
        toggled += 1;
        if (toggled <= n)
            setTimeout(toggle_one_time, 100);
    }
    toggle_one_time();
}

And just call toggle_multiple(4).

Pierre Bourdon
`toggled=0` probably should have a `var` to prevent an accidental global.
bobince
Right, edited my answer, thanks :)
Pierre Bourdon
Vitali
Edited, thanks for the (good) idea.
Pierre Bourdon
A: 

You can do like this:

function toggleMany(cnt) {
   toggle();
   if (--cnt >= 0) window.setTimeout('toggleMany('+cnt+')', 100);
}

toggleMany(4);
Guffa
remember that eval is evil! `window.setTimeout(toggleMany, 100, cnt)` is much neater, IMO anyway.
nickf
@nickf: Yes, but some browsers have problems with that. A string works everywhere.
Guffa
-1. You should always pass a proper JS function to setTimeout and not the JS code as string.
SolutionYogi
@SolutionYogi: You don't know what you are talking about. Originally the method didn't support a function pointer, only a string. I've seen function pointers still causing problems with some browsers, but a string always works.
Guffa
Yes, I am aware that a long time back, setTimeout only used to accept JS code as first parameter. But support for function as the first argument has been added since IE5/Netscape4 (Verified from MS and Mozilla's documentation). Nowadays, there is absolutely no reason to pass JS code to setTimeout as I believe that all the current browsers support setTimeout correctly. And even if there was an issue with setTimeout, you should have mentioned the proper solution and posted your version as an alternate workaround if the code doesn't work in the browser the OP is targetting.
SolutionYogi
+2  A: 

Why not use setInterval?

var toggler = function() {
    if (++self.counter >= self.BLINK_AMOUNT * 2) {
        self.counter = 0;
        window.clearInterval(self.timer);
        return;
    }
    toggle();
};
toggler.BLINK_AMOUNT = 1;
toggler.counter = 0;
toggler.timer = window.setInterval(toggler, 100);

I can't remember whether or not IE properly implements the self variable in a timer callback - if it doesn't, use a uniquely named global variable instead.

Vitali
`self` is the global (`window`) object in JavaScript. You are probably thinking of `this`. And no, `this` will not point to the toggler on any browser, but since you're just using a function rather than a full method, you can get that from `arguments.callee`.
bobince
Great idea! This is what you call 'thinking outside the box.'! :)
SolutionYogi
+1  A: 

Here's yet another version for simplicity:

for (var i= 0; i<4; i++)
    setTimeout(toggle, (i+1)*100);

For larger numbers an interval may be more appropriate, but if it's just four toggles multiple timeouts are fine.

bobince
if for some reason the browser slowed, or was busy, and couldnt execute one of the setTimeout, then all the timing is thrown off - you cannot guarantee that the toggle function is called `(i+1)*100` millisecond apart using this loop.
Chii
+2  A: 

A recursive approach:

function multiTimeoutCall (callback, delay, times) {
    if (times > 0){
      setTimeout(function () {
        callback();
        multiTimeoutCall (callback, delay, times - 1);
      }, delay);
    }
}

Usage:

multiTimeoutCall (toggle, 100, 4);

Edit: Yet another approach, without filling the call stack:

function multiTimeoutCall (callback, delay, times) {
  setTimeout(function action() { // a named function expression
    callback();
    if (--times > 0) {
      setTimeout (action, delay); // start a new timer
    }
  }, delay);
}

I could used arguments.callee instead of a named function expression, but seems that it will be deprecated some day in ECMAScript 5...

CMS
+1  A: 

Generalizing 'unknown's' idea of using setInterval,

function schedule(fn, max, delay)
{
    var counter = 0;
    var interval = setInterval(
        function()
        { 
            if(counter++ === max)
                clearInterval(interval);
            fn();
        }

       , delay);
}

Usage:

schedule(toggle, 4, 100);
SolutionYogi
A: 

If its just flashing that is required, why not use the jQuery animate ? I use the following to direct user attention to messages. But you can do this for any element -

$("#message_box").fadeOut(450).fadeIn(350);

If you want it multiple times, do this -

$("#message_box").fadeOut(450).fadeIn(350).fadeOut(450).fadeIn(350);

PlanetUnknown
Actually just saw Toby's reply above. Its a more elegant way. Let me up his reply.
PlanetUnknown