google-maps-api-3closuresclicklistener

Click Listeners in Loop - Array and Closure


I realise I'm treading on thin ice opening another closure issue, but I have searched and can't find an answer to my issue. I have a Google Maps API v3 page which generates two maps from one block of code - a small map centered on the user's current location and a larger map showing the whole area with the user's location marked where it is, center or not. On top of the map is a rectangle layer consisting of 14 rectangles. In order to generate the two maps, I have had to put the rectangles in a 2 dimensional array, rectangles[1] for 'map', and rectangles[2] for 'map2':

var rectangles = [0,1,2,3,4,5,6,7,8,9,10,11,12,13];

rectangles[1][0]=new google.maps.Rectangle({
bounds:new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
map:map,
fillColor:'red',
fillOpacity: 0.3,
strokeOpacity: 0,
url: 'http://example.com',
clickable: true
});

rectangles[2][0]=new google.maps.Rectangle({
bounds:new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
map:map2,
fillColor:'red',
fillOpacity: 0.3,
strokeOpacity: 0,
url: 'http://example.com',
clickable: true
});

...and so on. It all works fine and the two maps are displayed and the geolocation works. Now I want to add a click listener for each rectangle but I'm not sure who to reference the array. This is what I have now:

for ( i = 0; i < rectangles[1].length; i++ ){
google.maps.event.addListener(rectangles[1][i], 'click', function() {
window.location.href = this.url;
});
}
for ( x = 0; x < rectangles[2].length; x++ ){
google.maps.event.addListener(rectangles[2][x], 'click', function() {
window.location.href = this.url;
});
}

Which obviously won't work. I have seen various solutions to the closure issue, but I'm not sure I'm even heading in the right direction in referencing the two arrays of rectangles - or if I even need to define two different sets of click listeners. I'd be really grateful if someone could point me in the right direction - and sorry if this is just going over old ground that appears obvious. There's always a new learner coming along who is trying hard to catch up.

Thanks.


Solution

  • //First, set up `rectangles` as an array containing two arrays.
    var rectangles = [];
    rectangles[0] = [];
    rectangles[1] = [];
    
    //As `google.maps.Rectangle` doesn't accept a `url` option, 
    //its url needs to be defined separately from the rectangle itself,
    //but in such a way that the two are associated with each other.
    //For this we can use a javascript plain object. 
    rectangles[0][0] = {
        rect: new google.maps.Rectangle({
            bounds: new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
            map: map,
            fillColor: 'red',
            fillOpacity: 0.3,
            strokeOpacity: 0,
            clickable: true
        }),
        url: 'http://example.com'
    };
    rectangles[1][0] = new google.maps.Rectangle({
        ...
    });
    rectangles[0][1] = new google.maps.Rectangle({
        ...
    });
    rectangles[1][1] = new google.maps.Rectangle({
        ...
    });
    rectangles[0][2] = new google.maps.Rectangle({
        ...
    });
    rectangles[1][2] = new google.maps.Rectangle({
        ...
    });
    
    //Now to attach the click listeners.
    
    //First we define a function that adds a click listener.
    //By doing this in its own function, a closure is formed,
    //trapping the formal variable `rectObj` and making `rectObj.url` 
    //accessible to the listener when it is called in response to future clicks.
    function addClickListener(rectObj) {
        google.maps.event.addListener(rectObj.rect, 'click', function() {
            window.location.href = rectObj.url;
        });
    }
    
    //Now, we can loop through the `rectangles` arrays, adding listeners.
    for ( i = 0; i < 2; i++ ) {
        for ( j = 0; j < 14; j++ ) {
            if(rectangles[i][j]) {//safety
                addClickListener(rectangles[i][j]);
            }
        }
    }