Styphon
Styphon

Reputation: 10447

JavaScript variable scope - setTimeout stops code from seeing array

I'm working with the Google Map API v3 to try and populate a map with people advertising their availability for work. The map outputs fine and populates fine and I'm using the animate drop in feature to make them fall in nicely. However they all fall in one go and I want to make them fall one at a time, adding a simple delay is the solution and Google itself has that as an example. The only difference is I have an array full of results.

My current code

locations = [
    <?php foreach($searchResults as $clID => $r) { ?>
        ['<?php echo $r['clUserName']; ?>', <?php echo $r['clLat']; ?>, <?php echo $r['clLng']; ?>, '<?php echo '<div style="width: 100%"><div style="font-weight: bold; line-height: 25px;">' . $r['clUserName'] . '</div>Distance: ' . number_format($r['distance'], 0) . ' miles<br />Will Travel: ' . $r['clWorkRadius'] . ' miles<br />E-mail: <a style="color: #222 !important;;" href="mailto:' . $r['clEmail'] . '"><u>' . $r['clEmail'] . '</u></a><br />Tel: ' . $r['clTelephone'] . ($r['clMobile'] ? '<br />Mob: ' . $r['clMobile'] : '') . '</div>'; ?>'],

    <?php } ?>
    ];
    function initialize() {
        var mapOptions = {
            zoom: 10,
            center: new google.maps.LatLng(<?php if($_SESSION['search']['geo']['lat'] !== 0) { echo $_SESSION['search']['geo']['lat'] . ',' .$_SESSION['search']['geo']['lng']; } else { echo '51.507335,-0.127683'; } ?>),
            mapTypeId: google.maps.MapTypeId.ROADMAP
        }
        var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
        var infowindow = new google.maps.InfoWindow();
        var marker;
        for(i = 0; i < locations.length; i++) {
                marker = new google.maps.Marker({
                    position: new google.maps.LatLng(locations[i][1], locations[i][2]),
                    animation: google.maps.Animation.DROP,
                    map: map
                });

                google.maps.event.addListener(marker, 'click', (function(marker, i) {
                    return function() {
                        infowindow.setContent(locations[i][3]);
                        infowindow.open(map, marker);
                    }
                })(marker, i));
        }
    }
    function loadScript() {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = "http://maps.googleapis.com/maps/api/js?key=AIzaSyA6Bru5lLQukzGx3f-AVOBTPmxglkqI5m4&sensor=false&callback=initialize";
        document.body.appendChild(script);
    }
    window.onload = loadScript;

Code that breaks

To stagger the dropping of people on the map I added a setTimeout around the marker, delaying each one slightly longer. However doing so breaks the code. I keep getting a javascript error TypeError: locations[i] is undefined. Why would simply wrapping the code in a setTimeout stop it from being able to see locations?

        var marker;
        for(i = 0; i < locations.length; i++) {
            setTimeout(function() {
                marker = new google.maps.Marker({
                    position: new google.maps.LatLng(locations[i][1], locations[i][2]),
                    animation: google.maps.Animation.DROP,
                    map: map
                });

                google.maps.event.addListener(marker, 'click', (function(marker, i) {
                    return function() {
                        infowindow.setContent(locations[i][3]);
                        infowindow.open(map, marker);
                    }
                })(marker, i));
            }, 500);
        }

Upvotes: 0

Views: 532

Answers (1)

George
George

Reputation: 4257

You didn't wrap the callback you are passing to setTimeout in a self-evaluating function with the i variable, so by the time the callback was called, i === locations.length (the terminating case of the loop).

var marker;
for (i = 0; i < locations.length; i++) {
    setTimeout((function (i) {
        return function () {
            marker = new google.maps.Marker({
                position: new google.maps.LatLng(locations[i][1], locations[i][2]),
                animation: google.maps.Animation.DROP,
                map: map
            });

            google.maps.event.addListener(marker, 'click', (function (marker, i) {
                return function () {
                    infowindow.setContent(locations[i][3]);
                    infowindow.open(map, marker);
                }
            })(marker, i));
        };
    })(i), 500);
}

Upvotes: 1

Related Questions