0

I'm implementing a way to track clicks in a tabbed content section.

When I click any tab, the parameter is passing as clicked=tab4. It should be clicked=tab(value of 0-3). It looks like it's returning the length of the array? How can I make the event listener function keep the index value of the array.

Example: when I click tab1, the variable "i" in the event listener should be 0.

Here is my code. It works except for this one part, and I can't figure out why.

function clickTrack(){
    var tabs = ['tab1', 'tab2', 'tab3', 'tab4'];
    for(i = 0; i < tabs.length; i++) {
        document.getElementById(tabs[i]).addEventListener("click", function(){
                trackingFunction('param1','clicked=tab'+ i); 
                            alert(i);
                });

        }
}
  • [A dup](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example), though it is not about event handling, but you can apply the ideas. – Teemu Oct 08 '14 at 17:02

3 Answers3

0

You have to define an new execution context in order to save the state of the index variable like:

function clickTrack(){
        var tabs = ['tab1', 'tab2', 'tab3', 'tab4'];
        for(i = 0; i < tabs.length; i++) {
            (function(index){
            document.getElementById(tabs[i]).addEventListener("click", function(){
                    trackingFunction('param1','clicked=tab'+ index); 
                                alert(i);
                    });
            })(i);
            }
    }

An Immediately-Invoked Function Expression can be used to “lock in” values and effectively save state of the index variable.

Dalorzo
  • 19,834
  • 7
  • 55
  • 102
0

This would be a lot cleaner with forEach():

function clickTrack(){
    ['tab1', 'tab2', 'tab3', 'tab4'].forEach(function(tabId) {
        document.getElementById(tabId).addEventListener("click", function(){
            trackingFunction('param1','clicked=' + tabId);
            alert(tabId);
        });
    });
}

Note: Array#forEach is IE9+, so you would need to polyfill it for older browsers.


Regarding the why part of your question, let's look at a relevant subset of your code:

for(i = 0; i < tabs.length; i++) {
    document.getElementById(tabs[i]).addEventListener("click", function(){
        alert(i);
    });
}

The thing to note here is the closure that is created by click event handler. Specifically, it retains a reference to the i variable, which continues to be incremented until the i < tabs.length condition fails. All those increments happen synchronously, so by the time the click handler ever runs, it's guaranteed that the value of i will be tabs.length.

If you compare that with the forEach() example, you'll notice that the variable being enclosed by the click handler is tabId, which is a unique, new variable for each iteration, and therefore doesn't suffer from the same issues as the for loop.

jmar777
  • 38,796
  • 11
  • 66
  • 64
0

Replace "i" for its id...

http://jsfiddle.net/yzu7kLf4/

Try this:

function clickTrack(){
    var tabs = ['tab1', 'tab2', 'tab3', 'tab4'];
    for(i = 0; i < tabs.length; i++) {
        document.getElementById(tabs[i]).addEventListener("click", function(){
               trackingFunction('param1','clicked='+ this.id);
        });

    }
}
giordanolima
  • 1,190
  • 1
  • 12
  • 21