0

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);
            }

        });


    }


};

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

Oh thanks for your answer :) I must use setTimeout to call the function immediately i suppose ? Is there another solution without setTimeout ?
setTimeout simply simulates the callbacks (it calls the functions after the current execution queue is completed). It is not part of the solution. Pay attention to the differences between the two pieces of code posted - first is a simplification of what you are doing right now and second is the solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.