views:

104

answers:

2

I have some code that adds mouseover events and mouseout events to all 'a' tags on a page. I'd like it so that the mouseout starts a 5 second timer after which time it calls a function. However, if a new mouseover event fires, it should cancel any existing timers. The code I'm using follows. The setTimeout() is working fine, but it seems like the clearTimeout() isn't referencing the right timeoutID, even though I declared it globally. Any suggestions?

var timeoutID;

function addMouseoverEvent() {
    $('a').each(function(index) {
        $(this).mouseover(function() {
            clearTimeout(timeoutID);
            // do stuff
        })
    }); 
}

function addMouseoutEvent() {
    $('a').each(function(index) {
        $(this).mouseout(function() {
            timeoutID = setTimeout(function() {
                // do stuff
            }, 5000);
        })
    });
}

$(window).load(function() {
    addMouseoverEvent();
    addMouseoutEvent();
});

I should clarify that there should really only ever be one active timer. That's why I wanted it to be global. If a mouseover event occurs no timers should remain. And if a mouseout event occurs only one timer should be active - the one triggered by the last mouseout event.

A: 

If your timeoutId is globall then its going to get overwritten on each iteration of $('a').each(). If youre using 1.4 you can use the delay method most likely. or you could store the timeoutId on the element with $(this).data('timeoutId', setTimeout(youFunction)`.

prodigitalson
He's not assigning it in a `.each()` though, it happens in the handler...actually the `.each()` is extraneous, he can remove that closure completely.
Nick Craver
But its still iterating ig there is more than one `a` element in the retruned set... using `each` is redundant though.
prodigitalson
I've removed the .each() as per your suggestions. Better code, but the clearTimeout() still isn't working. If I store the timeoutID on each element, won't I need to iterate through every element's timeoutID when calling clearTimeout() in order to make sure I got them all?
dosboy
The `.data()` method did the trick, though I bound it to `$(document)` so that it had a global scope. Also had to call `clearTimeout($(document).data('timeoutID'))` before calling `setTimeout()` in the `mouseover` event. Thanks!
dosboy
@dosboy - The problem was the elements *inside* the anchors, since `mouseout` fires when **leaving a child** as well, you can probably get by with your original, just use `mouseenter` and `mouseleave` instead of `mouseover` and `mouseout` :)
Nick Craver
+2  A: 

I know it's already been answered, but I found that simply removing the .each() call makes this appear to work as desired. Try the little hover game on this Fiddle.

var timeoutID;

function addMouseoverEvent() {
  $('a').mouseover(function() { 
     clearTimeout(timeoutID);
  }); 
}

function addMouseoutEvent() {
  $('a').mouseout(function() {
     timeoutID = setTimeout(function() { 
       // do stuff
     }, 5000);
  });
}

$(document).ready( function(){
  addMouseoverEvent();
  addMouseoutEvent();
});

It's very possible I'm missing something -- but the hover game seems to work fine.

Ken Redler
Tested it out on jsFiddle and you're right, it does work there. I'm 90% positive it didn't work last night on my implementation though, but it was late and I was tired. +1 just for taking the time to test this and since it does seem like it works (at least in the fiddle sandbox).
dosboy