我想介绍一个功能,它允许标记的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都将指向最后一个标记。我的问题是,如何修改我的脚本以实现上述功能?
答案 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严格模式可以为你捕获偶然的全局变量。注意total
和i
也是偶然的全局变量。)
看起来addMarker
和addInfoname
已被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)
)
});
}