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);
	});
};

Numbered route same

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);
};

Numbered route good