Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: every event-handler defined in for-loop is the same, uses last iteration's values

I have trouble understanding the scoping rules in Javascript.

In the example below, I would assume that scope url variable is private in the for-loop. And that the onload-event function would see this private instance.

But things does not seems work like that - the alert will popup with the last url twice.

If somebody can clarify what is going on, I'll be grateful.

<html>
<head>
</head>
<body>
<script type="text/javascript">
    var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];
    for (var i=0;i<testArray.length;i++){
        var img = new Image();
        var url = testArray[i];
        img.onload = function(){
            alert(url);
        }
        img.src = url;
    }
</script>
</body>
</html>
like image 658
Mortennobel Avatar asked Aug 27 '26 23:08

Mortennobel


1 Answers

JavaScript does not have block-scope.

The only way to create new variable scope is in a function.

var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];

function createImg( url ) {
    var img = new Image();

    img.onload = function(){
        alert(url);
    }
    img.src = url;
    return img;
}
for (var i=0;i<testArray.length;i++){
    var img = createImg(testArray[i]);
}

Passing the testArray[i] to a function that creates and returns the new image ensure that the url referenced in the onload handler will be the one that was scoped in the function.


EDIT:

Ultimately, you'd never do this if all you need is access to the url.

You'd just get it from the property of the element via this.

function onloadHandler(){
    alert( this.src );  // <--- get the url from the .src property!
}

var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];
for (var i=0;i<testArray.length;i++){
    var img = new Image();
    var url = testArray[i];
    img.onload = onloadHandler;
    img.src = url;
}

This way you're not creating an identical handler function instance in the loop, but rather sharing the same instance, and referencing the element that received the event via this.

like image 122
user113716 Avatar answered Aug 29 '26 13:08

user113716



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!