Search code examples
javascriptvariablesevents

Javascript - use a variable's value (directly, not by reference) when declaring an asynchronous function


Alright so,

I have something that looks like this :

for (j = 0; j < btnArr.length; j++)
{
    var btn = document.createElement("button");
    btn.addEventListener("click", function() { press(this, j) }, false);
    div.appendChild(btn);
}

My problem is, since that the event happens at a late time, j has already changed value by time the event's function is triggered. I want the function to directly use j's value when the function is declared, and not a reference to j itself.

If addEventListener's 2nd parameter was a string, it would look like this:

btn.addEventListener("click", "function() { press(this, " + j + ") }", false);

Does anyone know if it's possible, and how to do this?

I tried searching around a bit, but I can't find anything relevant, since the problem is hard to describe properly with only a few words.

By the way, this is for a Greasemonkey script, hence the use of .addEventListener() instead of .onclick = (...)


Solution

  • You just need to break the context. It can be done a ton of ways, but Function.bind is the most elegant IMHO. Since you are in Firefox, bind should be there.

    for (j = 0; j < btnArr.length; j++)
    {
        var btn = document.createElement("button");
        btn.addEventListener("click", function(value) { press(this, value) }.bind(btn, j), false);
        div.appendChild(btn);
    }
    

    For an alternate approach, write a function that returns the handler.

    for (j = 0; j < btnArr.length; j++)
    {
        var btn = document.createElement("button");
        btn.addEventListener("click", getHandler(j), false);
        div.appendChild(btn);
    }
    
    function getHandler(j) {
        return function() { press(this, j) };
    }
    

    Yet another option is to add a property to the button element.

    for (j = 0; j < btnArr.length; j++)
    {
        var btn = document.createElement("button");
        btn.myValue = j;
        btn.addEventListener("click", function() { press(this, this.myValue); }, false);
        div.appendChild(btn);
    }