sidstory
百度地图js marker 点击窗口信息始终都是最后一次问题
作者:smice分类:Java
日期:2021-09-14 20:55:002021-09-14阅读:166
这几天有个需求,要在将某事件自定义标识且事件分布要在地图上显示出来,还要点击标志后显示具体信息。乍一看很简单,实则踩了百度地图的巨坑。
一般会这么写:
for (var p in datas) {
var positions = datas[p].q25;//获取事件经纬度
if (positions == ("")) {//跳过没包含经纬度
return;
}
var position_x = positions.split(",")[0]//经度
var position_y = positions.split(",")[1]//纬度
var point = new BMapGL.Point(position_x, position_y);
map.centerAndZoom(point, 15);
if (datas[p].q20 == "B") {//区分不同严重等级
icon_marker = bdicon2;
} else if (datas[p].q20 == "C") {
icon_marker = bdicon3;
} else
icon_marker = bdicon1;
markers[p]= new BMapGL.Marker(point, {icon: icon_marker});// 创建点标记
map.addOverlay(markers[p]);// 在地图上添加点标记
// 创建信息窗口
var opts = {
width: 200,
height: 100,
title: datas[p].q1//户主
};
infos[p]= new BMapGL.InfoWindow(datas[p].q6 + "<br>" + datas[p].q7 + "<br>" + datas[p].q13 + "<br>" + datas[p].q15, opts);
// 点标记添加点击事件
markers[x].addEventListener('click', function () {
markers[x].openInfoWindow(infos[x]);
});}
看似没有问题,一运行,窗口只会显示在最后一个添加的marker上,以为是自己问题,改了好久都不行。
百度了一看,这问题还挺多的,大致解决方案都是添加闭包,但都没说明原因。
写法如下:
for (var p in datas) {
var positions = datas[p].q25;//获取事件经纬度
if (positions == ("")) {//跳过没包含经纬度
return;
}
var position_x = positions.split(",")[0]//经度
var position_y = positions.split(",")[1]//纬度
var point = new BMapGL.Point(position_x, position_y);
map.centerAndZoom(point, 15);
if (datas[p].q20 == "B") {//区分不同严重等级
icon_marker = bdicon2;
} else if (datas[p].q20 == "C") {
icon_marker = bdicon3;
} else
icon_marker = bdicon1;
markers[p]= new BMapGL.Marker(point, {icon: icon_marker});// 创建点标记
map.addOverlay(markers[p]);// 在地图上添加点标记
// 创建信息窗口
var opts = {
width: 200,
height: 100,
title: datas[p].q1//户主
};
infos[p]= new BMapGL.InfoWindow(datas[p].q6 + "<br>" + datas[p].q7 + "<br>" + datas[p].q13 + "<br>" + datas[p].q15, opts);
// 点标记添加点击事件
(function (x) {
markers[x].addEventListener('click', function () {
markers[x].openInfoWindow(infos[x]);
});
})(p);
}
个人猜测是作用域和js线程的问题,众所周知js是单线程的。而百度地图的openInfoWindow是延迟方法,不使用数组的、闭包的话for循环已经执行完,值也被覆盖,而百度地图的openInfoWindow才执行,所以获取到的都是最后一次遍历的值。
这个bug跟一个很经典的js面试题很相似:
for(var i=0;i<5;i++){
setTimeout(function () {
console.log(i)}}
实际输出为五个5
为什么是五个5呢?因为js是单线程的,定时任务会放到一边等待执行,而for循环立即执行,同时因为i是var定义的,栈中维护的是同一个变量,等定时任务执行时,for循环已经执行完,i变为了5。
解决方法有两种:
第一种是将var改为let,因为let是块变量,只在块({}中)中生效,且有暂死性(不能重复定义),所以相当于console.log(i)中的i是5个不相同的对象。
第二种是添加闭包,如下:
for (var i=0;i<5;i++) {
(function (x) {
setTimeout(function () {
console.log(x)
},500)
})(i);
}
此时输出入下:
因为闭包会保存自己的变量,不会被垃圾回收器回收,这也是闭包用多了会对性能有影响的原因。
弹幕评论