1

I am having problems adding GMarkers when using a loop. The best way to explain the problem is to show the code, I guess :)

This works:

htmls[0] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[0].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[0].Username + "</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (" + result[1].Age + ")<br/>" + result[0].Country + "<br/>" + result[0].Distance + " KMs away<br/><a href=\"profile.aspx?lfid=" + result[0].Userid + "\">View Profile</a></td></tr></table></div>";
    latlngs[0] = new GLatLng(result[0].Latitude, result[0].Longitude);
    if (result[0].Gender == "F") {
        markers[0] = new GMarker(latlngs[0], { draggable: false, icon: fIcon });
    } else {
        markers[0] = new GMarker(latlngs[0], { draggable: false, icon: mIcon });
    }
    GEvent.addListener(markers[0], "click", function () {
        markers[0].openInfoWindowHtml(htmls[0]);
    });
    map.addOverlay(markers[0]);
    htmls[1] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[1].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[1].Username + "</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (" + result[1].Age + ")<br/>" + result[1].Country + "<br/>" + result[1].Distance + " KMs away<br/><a href=\"profile.aspx?lfid=" + result[1].Userid + "\">View Profile</a></td></tr></table></div>";
    latlngs[1] = new GLatLng(result[1].Latitude, result[1].Longitude);
    if (result[1].Gender == "F") {
        markers[1] = new GMarker(latlngs[1], { draggable: false, icon: fIcon });
    } else {
        markers[1] = new GMarker(latlngs[1], { draggable: false, icon: mIcon });
    }
    GEvent.addListener(markers[1], "click", function () {
        markers[1].openInfoWindowHtml(htmls[1]);
    });
    map.addOverlay(markers[1]);

But when I put it in a loop, it doesn't work...

for (i = 0; i < result.length; i++) {
        htmls[i] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[i].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[i].Username + "</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (" + result[i].Age + ")<br/>" + result[i].Country + "<br/>" + result[i].Distance + " KMs away<br/><a href=\"profile.aspx?lfid=" + result[i].Userid + "\">View Profile</a></td></tr></table></div>";
        latlngs[i] = new GLatLng(result[i].Latitude, result[i].Longitude);
        if (result[i].Gender == "F") {
            markers[i] = new GMarker(latlngs[i], { draggable: false, icon: fIcon });
        } else {
            markers[i] = new GMarker(latlngs[i], { draggable: false, icon: mIcon });
        }
        GEvent.addListener(markers[i], "click", function () {
            markers[i].openInfoWindowHtml(htmls[i]);
        });
        map.addOverlay(markers[i]);
    }

When using the loop, clicking on a marker breaks the script. It points to the line

markers[i].openInfoWindowHtml(htmls[i]);

And says that object is undefined. It also says that i = 10 at that point which is "impossible" as results.length is only 10

2 Answers 2

3

The problem is the classic function-in-a-loop. Here's one of the two typical ways to fix it:

function callback(i) {
    return function () {
        markers[i].openInfoWindowHtml(htmls[i]);
    };
}

for (i = 0; i < result.length; i++) {
    // snip...
    
    GEvent.addListener(markers[i], "click", callback(i));
    
    // snip...   
}

JSLint can easily catch these common errors.


Edit

@Alex's answer shows roughly the other typical way that this problem is fixed, but with a few errors. This should work, though:

for (i = 0; i < result.length; i++) {
    // snip...
    
    GEvent.addListener(markers[i], "click", (function (i) {
        return function () {
            markers[i].openInfoWindowHtml(htmls[i]);
        }
    })(i));
    
    // snip...   
}
Sign up to request clarification or add additional context in comments.

4 Comments

Oh yep, I fixed it at the same time too :) +1 for the hint
Thanks for the help, but that doesn't work either. var latlngs = [10]; var markers = [10]; var htmls = [10]; function callback(j) { return function (j) { markers[j].openInfoWindowHtml(htmls[j]); }; } function setIconsCB(result) { for (i = 0; i < result.length; i++) { ... GEvent.addListener(markers[i], "click", callback(i)); ... } Same problem... Object is undefined and i = 10
The function that callback returns should not take a parameter. Change function callback(j) { return function (j) { ... to function callback(j) { return function () { ... as in my answer.
OK guys, thanks a lot... I can get both versions to work now :) I guess I will use the nested function in the edited alex' answer. Thanks again.
1

In this piece of code...

GEvent.addListener(markers[i], "click", function () {
        markers[i].openInfoWindowHtml(htmls[i]);
});

...the function has closure to the i in the parent scope. So it is accessing the variable itself, not a copy of it.

At the end of the loop, when your function accesses the i variable, it will be equal to whatever condition stopped the loop, 10 in your example.

You can fix it with a self invoking anonymous function which passes the value to a new variable with a limited lifespan...

(function(j) {  
    GEvent.addListener(markers[j], "click", function () {
           markers[j].openInfoWindowHtml(htmls[j]);
    });
})(i);

Here is an example of similar code working.

1 Comment

Should htmls[i] be htmls[j] ?

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.