2

配列を関数に渡してから、その配列の情報を使用してGoogleマップを初期化しようとしています。ただし、マップ上のマーカーをクリックすると、次のようなエラーが発生します。

プロパティ'popupHtml'の値を取得できません:オブジェクトがnullまたは未定義です

関数を作成する理由は、javascriptコードを別の.jsファイルに移動して、htmlファイルとは別のものにするためです。とにかくこの問題を修正することはできますか?これが私のすべてのコードです(エラーが発生している場所をマークするためにコメントを入れました...):

<!DOCTYPE html>

<html>
<head>    
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        window.onload = function ()
        {
            function loadMap(markers)
            {
                var options =
                {
                    center: new google.maps.LatLng(40.775813, -73.970786),
                    zoom: 17,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                var map = new google.maps.Map(document.getElementById('map'), options);

                var points = new Array();
                for (var i = 0; i < markers.length; i++)
                    points.push(new google.maps.LatLng(markers[i].lat, markers[i].lon));
                var infoWindow = new google.maps.InfoWindow();
                for (var i = 0; i < markers.length; i++)
                {
                    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
                    google.maps.event.addListener(
                        googleMarker,
                        'click',
                        function ()
                        {
                            // The following line is where the error is occuring:
                            infoWindow.setContent(markers[i].popupHtml);
                            infoWindow.open(map, googleMarker);
                        });
                }
            };

            loadMap([{
                "lat": "40.776512",
                "lon": "-73.970293",
                "popupHtml": "\u003cdiv\u003eHello world - from marker 1!\u003c/div\u003e",
                "title": "Marker 1!"
            },
            {
                "lat": "40.774659",
                "lon": "-73.971548",
                "popupHtml": "\u003cdiv\u003eHello world - from marker 2!\u003c/div\u003e",
                "title": "Marker 2!"
            }]);
        };
    </script>
</head>
<body>
    <div id="map" style="width: 680px; height: 400px;"></div>
</body>
</html>
4

4 に答える 4

3

問題は、渡した関数が、関数が作成されたときの値のコピーではなく、変数addListenerへの永続的なアクセス権を持っていることです。したがって、関数のすべてのコピーは、呼び出された時点で表示されます。これは、おそらく配列の終わりを超えています。同じことが;にも当てはまります。現在の値ではなく、ループ内の最後の値がすべて表示されます。iigoogleMarker

ジェネレーター関数を使用して修正するか、(ECMAScript5に依存できる場合、またはbindシムが提供できるものであるためES5シムを使用する場合)を使用しますFunction#bind

使用bind

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
    google.maps.event.addListener(
        googleMarker,
        'click',
        (function (index, thisMarker)
        {
            // The following line is where the error is occuring:
            infoWindow.setContent(markers[index].popupHtml);
            infoWindow.open(map, thisMarker);
        }).bind(undefined, i, googleMarker)
    );
}

bindthis呼び出されると、指定された値と指定された引数を使用して元の関数を呼び出す関数を返します。したがって、上記では、とのbind渡すことを呼び出します。これにより、呼び出されたときに、これらの値を引数として元の関数を呼び出す関数が得られます。次に、との代わりに引数(と)を使用します。igoogleMarkerindexthisMarkerigoogleMarker

ターゲットブラウザのES5機能に依存できず、シムを使用したくない場合は、ジェネレータ関数を使用できます。

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
    google.maps.event.addListener(
        googleMarker,
        'click',
        makeHandler(i, googleMarker)
    );
}

function makeHandler(index, thisMarker) {
    return function ()
    {
        // The following line is where the error is occuring:
        infoWindow.setContent(markers[index].popupHtml);
        infoWindow.open(map, thisMarker);
    };
}

そこで、とのmakeHandler渡して、を呼び出し、とではなく、これらの引数(と)を閉じる関数を返します。引数は変更されないため、関数は正しい値を認識します。igoogleMarkermakeHandlerindexthisMarkerigoogleMarkermakeHandler

これはすべて、クロージャがどのように機能するかに関係しています。クロージャーの詳細:クロージャーは複雑ではありません

于 2012-05-01T08:59:20.643 に答える
0

loadMap関数の定義はwindow.onloadハンドラーにあるため、ページが読み込まれるまで使用できません。その場合でも、関数は別のスコープにあります。

少し逆行していると思います。loadMap関数の定義をloadMapの呼び出しと交換して、呼び出しがonloadイベント内にあり、定義がそうではないようにします。

于 2012-05-01T09:02:16.033 に答える
0

TJ Crowderが指摘しているようiに、クロージャー内であっても、ループが進むにつれて値が変化します。彼の答えと同様に、これを修正する簡単なアプローチは、ループのスコープ内に変数を作成することです。

for (var i = 0; i < markers.length; i++)
    {
        var marker = markers[i]; // new variable is created for each loop iteration
        var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: marker.title });
        google.maps.event.addListener(
            googleMarker,
            'click',
            function ()
            {
                // The following line is where the error is occuring:
                infoWindow.setContent(marker.popupHtml);
                infoWindow.open(map, googleMarker);
            });
    }
于 2012-05-01T09:06:51.697 に答える
0

単純な無名関数を使用できるため、ここで関数を実行すると、その実行のコンテキストがステップ値を保持します。

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: marker.title });

    (function(step){google.maps.event.addListener(
        googleMarker,
        'click',
        function ()
        {
            // The following line is where the error is occuring:
            infoWindow.setContent(markers[step].popupHtml);
            infoWindow.open(map, googleMarker);
        });
    )(i);
}
于 2012-05-01T09:21:08.540 に答える