Benjamin B.
Benjamin B.

Reputation: 483

addEventListener with event name variable inside a loop

i want to apply event listeners in javascript to elements inside a loop. The event name is variable in the loop. When i try with my code it's not working only the last addEventListener is working ? The events i want to add are : click and mouseenter. When i click on the item, it's the mouseenter event which which is fired !

_apply = function(trackEventData, trackEventItem) {

    var tmpData, i, j, eventType, eventAction;

    // get stringified json events
    tmpData = JSON.parse(trackEventData);
    for (i = 0; i < tmpData.length; i++) {
        // for each type in events

        eventType = Events.getEventTypeByKey(tmpData[i].key);
        eventAction = tmpData[i].actions;
        console.log('apply ' + eventType.event);
        // we add the event listener

        trackEventItem.addEventListener(eventType.event, function(e) {
            console.log('eventfired ' + eventType.event);
            e.preventDefault();
            // and for each action we add the function callback
            for (j = 0; j < eventAction.length; j++) {
                Events.getEventActionByKey(eventAction[j].value).action(eventAction[j].options);
            }

        });


    }


};

Upvotes: 0

Views: 883

Answers (1)

Tibos
Tibos

Reputation: 27823

Your problem is one of closure.

Simplified, your code looks like this:

var events = ['click', 'mousedown'];
for (var i=0; i<events.length; i++) {
  var a = events[i];
  window.setTimeout(function(){
    console.log(a);
  },0);
}

The function there logs the variable defined in the outer scope. Both functions created (one for each iteration) print the same variable. At the time the functions are created the variable has different values, but when they are called, the variable has the value 'mousedown', so they will both log 'mousedown'.

What you want to do is create a local version of the a variable to each function. But since it is a callback you're writing there, you can't alter the parameters. The solution is to wrap it in another function and use closures again (but properly this time):

for (var i=0; i<events.length; i++) {
  var a = events[i];
  window.setTimeout(function(obj){ return function(){
    console.log(obj);
  }}(a),0);
}

As you can see, you created a scope for each iteration where you save the current value of a to obj (by passing it as parameter to an anonymous function). Your actual handler uses the value in that scope, particular to the iteration.

Demo: http://jsbin.com/agoDaza/1/edit

Upvotes: 2

Related Questions