在动态生成的DIV的鼠标悬停上显示Google Maps Marker infoname

时间:2011-10-01 15:07:08

标签: javascript jquery google-maps-api-3

我想介绍一个功能,它允许标记的infoname在鼠标悬停或从jQuery生成的相应DIV元素的鼠标输出时显示或消失。但是,我在main.js的第19行收到了“ a is undefined ”错误。经过对我的脚本的大量测试后,我意识到这与新添加的行中的标记有关,如下所示:

function addMarker(A) {
 var point = new google.maps.LatLng(A.lat, A.lng);      
 var image = new google.maps.MarkerImage('images/r.png',
  new google.maps.Size(30, 30),
  new google.maps.Point(0, 0),
  new google.maps.Point(0, 30));
 marker = new google.maps.Marker({
  map: map,
  position: point,
  icon: image,
 });
}

function addInfoName(A) {
 var infoname = new infoName; // custom object
 google.maps.event.addListener(marker, 'mouseover', function(event) {infoname.show();});
 google.maps.event.addListener(marker, 'mouseout', function(event) {infoname.hide();});
 infoname.open(map, marker);
}

function showResult(A) {
 $('#results').append('<DIV id=' + A.pid + '>{Blah Blah Blah}</DIV>');
 return document.getElementById(A.pid);
}

function process(json) {
 $('#results').empty();
 total = json.details.length;
 for(i=0; i<total; i++) {
  var detail = json.details[i];
  var marker;
  addMarker(detail);
  addInfoName(detail);

// these new lines are added
  var listDisplay = showResult(detail);
  listDisplay.onmouseover = function(){google.maps.event.trigger(marker, 'mouseover');};
  listDisplay.onmouseout = function(){google.maps.event.trigger(marker, 'mouseout');};
 }
}

google.maps.event.addListener(map, 'idle', function () {$.getJSON(query, process);});

如果我将函数addInfoName合并到process,则错误消失。但是,如果我这样做,所有DIV都将指向最后一个标记。我的问题是,如何修改我的脚本以实现上述功能

2 个答案:

答案 0 :(得分:1)

“a is undefined”错误可能是因为您在dom准备好之前尝试创建地图。至少,这是我唯一一次看到它。我无法从您的代码中知道您在哪里创建它,但请确保map div已准备就绪。您必须将调用放在页面底部或页面加载侦听器中的初始化函数中。这是你可以做到的一种方式(这可以在页面的任何地方):

function initialize() {
    var map = new google.maps.Map(document.getElementById("map_canvas"), {
        zoom: 6,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    google.maps.event.addListener(map, 'idle', function () {
        $.getJSON(query, process);
    });
}
google.maps.event.addDomListener(window, 'load', initialize);

另请注意,您的空闲侦听器也会进入该init函数,因为在创建映射之前它将无法运行。

如果这不会导致“a is undefined”错误,那么我在您发布的代码中看不到它。但是,我确实看到了代码中的其他一些问题。所以也许这实际上是造成它的原因。首先,var marker;中的process定义没有做任何事情。在这里,您将创建一个局部变量,但该局部变量永远不会被定义。然后addMarker通过定义marker而不是var来创建全局变量。因此addInfoname中的标记始终引用全局标记,该标记始终是定义的最后一个标记。所以这就是为什么div总是出现最后一个标记。我会在addMarker中marker = ...之前返回一个返回值,并使用它来设置这样的标记变量:

var marker = addMarker(detail);

当然是process。然后你还必须将它发送到addInfoname作为参数,以便它得到正确的。

答案 1 :(得分:1)

目前,您已经为marker函数声明了一个变量process,但您正试图从其他函数读取和写入它。特别是,addMarker在没有marker的情况下写入var,这会导致创建意外的全局变量。同时,process实际上并未写入其声明的marker本地,因此它包含undefined,当您将其传递出去时,它会使Google地图代码重新启动。

(像jslint这样的工具,或ECMAScript 5严格模式可以为你捕获偶然的全局变量。注意totali也是偶然的全局变量。)

看起来addMarkeraddInfoname已被process的主体攻击,而没有捆绑他们两个使用的process变量。如果它们被包含在process的主体中它将起作用,但是你会得到所描述的行为,因为闭包循环问题,每个div使用相同的marker值。

在具有闭包和功能级范围的语言中会出现此问题,其中包括JavaScript,Python等。在这些语言中,由for循环定义或内部定义的任何变量都是包含函数的本地变量,每次循环时都会重新分配 。因此,如果在循环的第一次迭代中引用i的闭包,则它与循环的第二次迭代中引用的变量i相同;函数的每个实例都在同一个变量i上有一个闭包,所以每个函数都会看到相同的值。 marker也是如此。

闭包循环问题可以通过使用第二个闭包来避免,该闭包将循环变量保存在参数中,或者更干净地使用基于闭包的循环机制而不是类似C的for循环。 ECMAScript 5为此目的提供array.forEach(),jQuery提供$.each()

function process(json) {
    $('#results').empty();
    var gev= google.maps.event;
    $.each(json.details, function(detaili, detail) {
        var marker= addMarker(detail.lat, detail.lng);

        $('#results').append($('<div>', {
            text: detail.name,
            mouseover: function() { gev.trigger(marker, 'mouseover'); },
            mouseout: function() { gev.trigger(marker, 'mouseout'); }
        }));

        var infoname= new InfoName();
        gev.addListener(marker, 'mouseover', function() { infoname.show(); });
        gev.addListener(marker, 'mouseout', function() { infoname.hide(); });
        infoname.open(map, marker);
    });
}

function addMarker(lat, lng) {
    return new google.maps.Marker({
        map: map,
        position: new google.maps.LatLng(lat, lng),
        icon: new google.maps.MarkerImage(
            'images/r.png',
            new google.maps.Size(30, 30),
            new google.maps.Point(0, 0),
            new google.maps.Point(0, 30)
        )
    });
}