Click Listeners in Loop - Array and Closure

2019-03-03 12:21发布

问题:

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.

回答1:

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