A JavaScript gotcha – Defining a function in a for loop
No JavaScript programmer am I….but I have picked up bits and pieces as I’ve needed to add functionality to websites I occasionally build. I was recently adding a bit of mapping functionality to my baseball road trip planner.
I was using Leaflet and D3, which are JavaScript libraries for mapping and visualization, respectively. These are really great libraries, and have allowed me to implement mapping functionality faster than I would have thought possible. However in the course of implementing the maps, I was bitten by a much more prosaic JavaScript error that I thought I would document here.
My goal was to show a given road trip on a map, with numbered Leaflet markers indicating the order in which the cities are visited. (see here for a clever way to use numbered markers in Leaflet). I had a stadium GEOJSON file for the location of each stadium, but since I didn’t know the order in which the stadiums were visited, I couldn’t assign that order within the GEOJSON file.
Easy enough though, right ?
I would just loop through the city and assign the marker number as I put them on the map as follows. Here’s an attempt to put the first 8 stadiums on the map (this just uses the first 8 numeric stadiums in my application in order which are Anaheim, Arizona, Atlanta, Baltimore, Boston, Chicago Cubs, Chicago White Sox and Cleveland)
for (var k = 1; k <=8 ; k++) {
std_fname = "/static/js/GEOJSON/stadium_" + k + "_geojson.json";
d3.json(std_fname, function (error,data) {
var stadia = new L.geoJson(data, {
onEachFeature: onEachFeature1,
pointToLayer: function(feature, latlng) {
return new L.Marker(latlng,
{icon: new L.NumberedDivIcon({number: k.toString()})});
}
}).addTo(map);
});
};
Clearly, that didn’t work out so great ! As an experienced programmer but fairly inexperienced JavaScript user, this was driving me crazy, causing me to question whether I even understood how a basic for loop works. However, after trying many possibilities and no small amount of Googling, I found information here that lead to my solution (see Common Mistake #6, though all are worth knowing).
What needed to be done was to take the function definition outside the loop.
I can’t claim to fully understand this, but it solved the problem at hand. (Note that marker 6 doesn’t show up on the following screenshot since it is obscured by marker 7; this is the two Chicago stadium within 8 miles of each other, which would be clear upon zooming).
function makeLayer(pfile,pmap,pnum) {
d3.json(pfile, function (error,data) {
var stadia = new L.geoJson(data, {
onEachFeature: onEachFeature1,
pointToLayer: function(feature, latlng) {
return new L.Marker(latlng,{icon:
new L.NumberedDivIcon({number: pnum.toString()})});
}
}).addTo(pmap);
});
}
for (var k = 1; k <=8 ; k++) {
std_fname = "/static/js/GEOJSON/stadium_" + k + "_geojson.json";
makeLayer(std_fname,map,k);
};