Escher
Escher

Reputation: 5776

Applying a mouseover event listener inside of a loop

I have a button (<a class="gmButton"></a>) and a <span id="gmToolTip"></span>, and I'd like the span to display certain text when the relevant link is mouseovered.

The text to display is an array of strings called toolTips.

gmButtons[i].addEventListener("mouseover", function(){
    clearTimeout(t);
    t = setTimeout(function() { 
        gmToolTip.textContent = toolTips[i];
    }, 500);
});
gmButtons[i].addEventListener("mouseout", function(){
    gmToolTip.textContent = null;
    clearTimeout(t);
});

When applied to the links one-by-one, the code seems to perform as expected. It's not working when applied in a loop like this. What have I screwed up?

Here's the fiddle: http://jsfiddle.net/d5tpqt5h/1/

Upvotes: 2

Views: 1018

Answers (3)

Pao Im
Pao Im

Reputation: 347

Try with innerHTML and closure:

for (var i = 0; i < gmButtons.length; i++){
    (function(i) {
      gmButtons[i].addEventListener("mouseover", function(){
        clearTimeout(t);
        t = setTimeout(function() {
            gmToolTip.innerHTML = toolTips[i];
        }, 500);
    });
    gmButtons[i].addEventListener("mouseout", function(){
        gmTooltip.innerHTML = null;
        clearTimeout(t);
    });
    })(i);
}

Demo

Upvotes: 0

Josh Crozier
Josh Crozier

Reputation: 240968

The issue is that the event listener functions are fired after you have already looped over all of the element. This means that when they are called, i is equal to 9 (and toolTips[9] is undefined because the last element's index in an array is one less than its length).

One option is to wrap the logic in an IIFE in order to capture the current value of i:

Updated Example

for (var i = 0; i < gmButtons.length; i++) {
  (function(i) {
    gmButtons[i].addEventListener("mouseover", function() {
      clearTimeout(t);
      t = setTimeout(function() {
        gmToolTip.textContent = toolTips[i];
      }, 500);
    });
    gmButtons[i].addEventListener("mouseout", function() {
      gmToolTip.textContent = null;
      clearTimeout(t);
    });
  })(i);
}

Alternately, you could also utilize the .bind() method in order to pass the current value of i to the function:

Updated Example

for (var i = 0; i < gmButtons.length; i++) {
  gmButtons[i].addEventListener("mouseover", function(i) {
    clearTimeout(t);
    t = setTimeout(function() {
      gmToolTip.textContent = toolTips[i];
    }, 500);
  }.bind(this, i));
  gmButtons[i].addEventListener("mouseout", function() {
    gmToolTip.textContent = null;
    clearTimeout(t);
  });
}

Upvotes: 6

krishnaxv
krishnaxv

Reputation: 956

Change gmTooltip.textContent = null; to gmToolTip.textContent = null;

gmTooltip to gmToolTip

Hope it helps!

Upvotes: 1

Related Questions