JSLint Messages – Don’t make functions within a loop.

In JavaScript we can declare a function anonymously, and using a concept known as late-binding we can attach the event to an HTML object:

document.getElementById("control").onclick = function(){//code};

We can also conceive a situation whereby we can do this to multiple controls within a loop. Casting aside the capabilities of JavaScript libraries such as jQuery for the moment (we’ll examine jQuery  later), the code might look something like:

var x=0;
for(x = 1; x<4; x++){
    document.getElementById("text"+x).onclick = function(){
        alert("test");
    };
}

Herein lies the complaint of JSLint: “Don’t make functions within a loop.” In the above code, on every iteration of the loop we will evaluate the function. This is wasteful and unnecessary. It’s much better simply to store the function in a variable:

var x=0;
var f = function(){
    alert("test");
};

for(x = 1; x<4; x++){
    document.getElementById("but"+x).onclick = f;
}

For many cases this will be sufficient,  but the above ignores the frequent requirement to pass values to functions. So how can we do this?

Well, in the above example a parameter will be passed to the function automatically. This parameter will be the event object that represents the source event. From this object we can infer the event source (e.g. an HTML button), and from the event source, we can infer object data.

Consider the following code (See it work at this JSFiddle):

var x=0;
var f = function(evt){
    evt.target = evt.target ? evt.target : evt.srcElement;  //cross-browser event target retrieval
    alert(evt.target.getAttribute("data-info"));
};

for(x = 1; x<=3; x++){
    var objButton = document.getElementById("but"+x);
    objButton.setAttribute("data-info", x);  //set a 'data-'attribute
    objButton.onclick = f;
}

Here we bind the data (x) to the control using the setAttribute() function on the object. We can retrieve this by calling getAttribute on the event target when the event fires. Refer to the JavaScript kit event object for more information on JavaScript events.

Or, of course, we can disregard the event obejct altogther, and instead use the JavaScript this object (available at this slightly updated fiddle)

var x=0;
var f = function(evt){
    alert(this.getAttribute("data-info"));
};

for(x = 1; x<=3; x++){
    var objButton = document.getElementById("but"+x);
    objButton.setAttribute("data-info", x);
    objButton.onclick = f;
}

The JavaScript this object is discussed in more depth over at QuirksMode.org.

And what about jQuery? Well, this can actually hide this sort of problem from JSLint. Consider the following:

$("input:button").each(function () {
    $(this).click(function () {});
});

This slightly contrived example (contrived because we don’t need to call the .each() to bind for all elements in the collection) will pass a JSLint scan, and yet, still break the rule that it was trying to preserve. We are still evaluating a function for every call of .each(), but JSLint doesn’t (and can’t) know this .each() functions as a loop.

But, adopting the pattern above in the same manner will solve this problem.

A Guide To JSLint Messages

This article is one of a series on the error and warning messages produced by JSLint.

2 thoughts on “JSLint Messages – Don’t make functions within a loop.

  1. Thanks for the article (though I come a little late).

    To just end your explaination you can just use the parameters sent by jQuery.each to do the exact same thing :

    var f = function(index, el) {
    // el is the HTML element
    // $(el) is the jQuery counterpart
    }

    $(“input:button”).each(f);

    JSLint will approve and you will have created only one function.

  2. Pingback: JSLint – A Guide To JSLint Messages | James Wiseman

Leave a Reply