百度地图 API 详解之事件机制
2012-03-09 12:53:20| 分类: 学习|字号 订阅
和 DOM 编程里的事件模型一样,百度地图 API 也提供了类似的事件机制。本文介绍了事件监听的添
加和移除方法,this 指针和事件参数的使用以及绑定事件监听函数中涉及的闭包问题,最后分享了一
个用来增强地图 API 事件机制的开源项目。
事件添加和移除
我们最简单的事件开始,下面的代码示例给 map 对象添加了 click 事件的监听函数,当用户点击地图
时该监听函数就会被触发:
var map =new BMap.Map('map');
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);
map.addEventListener('click', function(){
alert('您点击了地图');
});
在初始化地图完成后,我们通过 map 对象的 addEventListener 方法添加 click 事件的监听。凡是在类
参考文档中说明某个类具备某些事件时,我们都可以调用该对象的 addEventListener 方法添加响应
的事件监听函数。
上面我们通过传入一个匿名函数添加了事件监听,如果我们需要移除事件监听,则需要将监听函数用
具名函数表示:
var clickHandler =function(){
alert('您点击了地图');
}
map.addEventListener('click', clickHandler);
// 后续进行移除
map.removeEventListener('click', clickHandler);
事件参数
和 DOM 事件类似,在百度地图 API 的事件机制中也提供了事件参数,参数通过监听函数的参数进行
传递,比如我们修改最开始的代码:
map.addEventListener('click', function(e){
alert('点击坐标: '+ e.point.lng +', '+ e.point.lat);
});
通过 API 的类参考文档得知,click 事件的参数包含 type、target、point 和 pixel 四个属性,上面的示
例就是获取了 point 属性,它表示当前点击的地理位置。
this
在事件监听中还可以通过 this 引用触发事件的对象,就像标准的 DOM 事件一样。
map.addEventListener('dragend', function(e){
alert(this.getCenter()); // 这里的 this 就是 map 实例
});
事件闭包
闭包是 Javascript 脚本语言的特性之一,不熟悉它的开发者很可能犯下面这种错误:页面有十个标注
(Marker 实例),我希望点击不同的标注开启内容不同的信息窗口,每个信息窗口显示该标注的索
引(1 到 10)。首先我们初始化标注实例并存放于数组中:
var markers = [];
for (var i =0; i <10; i ++) {
var mkr =new BMap.Marker(new BMap.Point(116.2+ i /20, 39.855), {title: i +1});
markers.push(mkr);
map.addOverlay(mkr);
}
我们给每个标注添加自己的 title 属性以示区分,你将看到如下效果:
下面,我们添加标注的点击事件,用来开启内容不同的信息窗口:
for (i =0; i <10; i ++) {
markers[i].addEventListener('click', function(){
this.openInfoWindow(new BMap.InfoWindow('我是第'+ (i +1) +'个标注'));
});
}
代码看起来没有任何问题,循环遍历 markers 数组,为每个标注实例绑定 click 事件,事件处理函数
中开启信息窗口并显示是第几个标注。但是通过浏览器看效果的时候却发现问题了:
明明是第三个标注,点击之后的信息窗口中却显示“11”。实际上,当 click 的监听函数被执行的时候
才会去看变量 i 的值是什么,此时 for 循环早已经执行完,那么当 for 循环执行完 i 的值正好是 11。为
了达到我们想要的效果,需要增加一层“闭包”:
for (i =0; i <10; i ++) {
(function(){
var index = i;
markers[i].addEventListener('click', function(){
this.openInfoWindow(new BMap.InfoWindow('我是第'+ (index +1) +'个标注'));
});
})();
}
上面的代码你可以理解为新增加的函数内部保存了每次循环变量 i 的值,那么当监听函数执行时将会
获取闭包内保存的 index 变量的值,而不是之前的变量 i 的值。再看一下,效果 OK 了:
对地图 API 事件增强
熟悉 google 地图 API 的人可能知道,其事件相关的接口要比百度地图 API 的 接口丰富,除了提供
简单的 addListener 和 removeListener 之外,还提供了 clearListeners、 clearInstanceListeners、
trigger 等方法。那么这些功能在百度地图 API 中如何实现呢?实际上百度地图 API 的内部也有事件 机
制并且与对外公开的事件共用一套机制,如果 API 提供了诸如 clearListeners 的方法必然会对 API 内
部的逻辑造成影响,因此也就没有提供类 似的接口。如果开发者很喜欢 google 地图 API 的事件机制
模式并且非用不可怎么办呢,没问题,这里和大家分享一个我的开源项目,名字暂定为 EventWrapper,
它基于百度地图 API,但提供了类似 google 地图事件机制的接口形式。开源项目地址:
https://github.com/jiazheng/EventWrapper。src 目录下的 main.js 就是全部代码了,这是开发版本,
如果直接使用可以获取 release 目录下的 eventwrapper.min.js。
我们通过几个例子来看一下它的使用方法,首先需要引用这个 js 脚本,这里就直接使用 github 提供
的原始数据的地址:
下面我们添加两个事件监听:
var clickListener = EventWrapper.addListener(map, 'click', function(e){
alert(e.point.lng +', '+ e.point.lat);
});
var dragListener = EventWrapper.addListener(map, 'dragend', function(e){
alert(e.point.lng +', '+ e.point.lat);
});
移除的方式如下:
EventWrapper.removeListener(dragListener);
我们将之前 addListener 方法返回的对象传递给 removeListener 即可,这和 google 的使用方式一致。
此外我们还可以清除某个对象所有通过此方式绑定的事件:
EventWrapper.clearInstanceListeners(map); // 清除 map 实例所有的事件监听函数
此外 EventWrapper 还提供了 addDomListener、addDomListenerOnce、addListenerOnce、
clearListeners 和 trigger 方法,具体使用方法可参考开发版本脚本中的注释信息(开发脚本原始数据
地址:https://raw.github.com/jiazheng/EventWrapper/master/src/main.js)。
目前此项目基本完成,但是还没有经过大规模的测试和验证,可能存在不完善的地方,也欢迎广大开
发者发现问题并进行反馈。
补充一个很多人都在问的问题
很多人在使用 API 时发现我绑定了 map 和一 个 marker 的 click 事件,发现点击 marker 的时候,不
仅 marker 的 click 事件会被触发,map 的 click 事件也会被触发。API 会 将事件向上传递,实际上点
击任何覆盖物都会向上传递到 map。那问题是怎么区分呢?map 的 click 事件的事件参数 e 中包含一
个名为 overlay 的属 性,所以只需要在事件处理函数中判断 overlay 是否存在即可区分。