简体   繁体   中英

PHP javascript closure issue with google maps v3

My application plots a bunch of markers on a map read from a MySQL database, and I want to display some content in each of the infowindows. My issue is the infowindows will only display the content of the last entry in my database. Below is my code, where I simply try to display the name of the location. I've tried alerting the variables to see if they are indeed displaying the correct content, which they are. I realize it is a javascript closure problem, but I don't know how to go about fixing this.

var startLat;
var startLng;
var locationName;

<?php foreach($workstations as $workstation):?>
    startLat = "<?php echo $workstation['lat']?>";
    startLng = "<?php echo $workstation['lng']?>";
    locationName = "<?php echo $workstation['name']?>";

    var marker = new google.maps.Marker({
        position: new google.maps.LatLng(startLat, startLng),
        map: map,
        icon: "images/purple.png"
    });

    //load all the infowindow content
    var contentString = [
                    '<div id="InfoText" style="margin: 0px; padding: 0px; overflow: hidden; border:0px">',
                    '<div class="tabs"><ul><li><a href="#tab1">General</a></li>',
                    '<li><a href="#tab2">General 2</a></li></ul>',
                    '<div id="tab1">',
                    '<b>' + locationName + '</b> - ' + locationName + '<BR>',
                    '<input id="save" type="submit" value="Reserve Today" onclick="saveStation(addMarker)" />',
                    '</div>',
                    '<div id="tab2">',
                    '</div>',
                    '</div>'
                           ].join('');

    //alert(contentString);
    var infowindow = new google.maps.InfoWindow({content: contentString});

    new google.maps.event.addListener(marker, 'click', (function(marker) {
        return function(){
            infowindow.open(map, marker);
            }
        })(marker));
<?php endforeach;?>

Yes. This is a js closure issue. You have one variable "marker" which all of the maps event listeners are pointing to. Try this instead:

Place this at the top (before the foreach loop):

function addMarkerListener( mark, mp, win )
{
    new google.maps.event.addListener(
          mark, 
          'click', 
          function(){win.open(mp,mark)}
}

Then, instead of the listener at the end of your code block add this:

addMarkerListener( marker, map, infowindow );

Basically, what is happening in your version is that js is saying, "Ok, I'm going to create a function and have the marker variable populate it. Got it." When the time comes to actually call the function, it says, "I'm looking for the variable 'marker', what's in that variable?" Unfortunately, this has been re-assigned in the meanwhile and now it represents the most recent assignment.

The version here, on the other hand, tells JavaScript, "I have these brand new variables, they only exist here, so don't look for them anywhere else. When one of them is clicked, then display the value related to it." When the item is clicked, JS says, "Hmm... this variable was only assigned to be used in this context so I guess I will use that."

I could go into variable binding, and there is some exciting Lambda Calculus stuff involved, but that is a bit much for this scope.

You should not do the looping in PHP. Not only are you outputting massive, repetitive Javascript blocks this way, you're also producing this:

var infowindow = …
var infowindow = …
var infowindow = …
var infowindow = …

You're overwriting your own infowindow variable, so only the last one sticks, obviously.

Just prepare the data in PHP, then use a Javascript loop to process it. This should also make scope issues more obvious. In your case, the infowindow variable has one and can be fixed as below:

var workstations = <?php echo json_encode($workstations); ?>;

for (var i = 0; i < workstation.length; i++) {
    var lat = workstations[i].lat;
    var lng = workstations[i].lng;
    var name = workstations[i].name;

    var marker = …
    var content = …
    var infowindow = … 

    (function (marker, infowindow) {
        google.maps.event.addListener(marker, 'click', function () {
            infowindow.open(map, marker);
        });
    })(marker, infowindow);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM