events
本文回答:Marker 事件 —— click / mouseover / mouseleave / contextmenu / dragstart / drag / dragend / autoAdd / autoRemove。
一、Marker 本身的事件(marker.on)
| 事件 | 触发时机 |
|---|---|
dragstart |
拖拽开始 |
drag |
拖拽中(频繁触发) |
dragend |
拖拽结束 |
autoAdd |
自动插回 DOM(removeWhenNoInMapView 回到视野,或 minZoom/maxZoom 回到区间) |
autoRemove |
自动摘除 DOM(离开视野或超出缩放区间) |
click / dblclick / contextmenu / mousedown / mouseup / mousemove / mouseover / mouseleave / touchstart / touchmove / touchend |
element 上对应 DOM 事件的转发(hover 用 mouseover,没有 mouseenter) |
marker.on("dragend", e => { /* e.target 即被拖动的 marker(事件是裸 Event,无 e.marker 字段) */ });
marker.on("autoAdd", () => console.log("进入视野"));
marker.on("autoRemove", () => console.log("离开视野"));
marker.on("click")等转发事件的参数是把原生 DOM 事件字段逐个复制出来的普通对象:e.clientX/e.button等可读,e.target是 marker 实例;没有originalEvent/lngLat。复制来的e.stopPropagation()/e.preventDefault()一调就抛Illegal invocation——要阻止冒泡用 §二的 element DOM 监听。
二、DOM 事件(通过 element)
Marker 的 click / hover 通过 DOM 监听:
const el = marker.getElement();
el.addEventListener("click", e => {
e.stopPropagation(); // 阻止冒泡到地图
alert("Marker clicked");
});
el.addEventListener("mouseenter", () => {
el.style.background = "#ff0";
});
el.addEventListener("mouseleave", () => {
el.style.background = "";
});
el.addEventListener("contextmenu", e => {
e.preventDefault();
alert("右键");
});
三、click 传递经纬度
const el = marker.getElement();
el.addEventListener("click", () => {
const ll = marker.getLngLat();
alert(`点击 (${ll.lng}, ${ll.lat})`);
});
四、hover 联动 Popup
const popup = new vjmap.Popup({ closeButton: false, offset: [0, -40] })
.setHTML("<b>站点 A</b><br/>状态:正常");
const el = marker.getElement();
el.addEventListener("mouseenter", () => {
marker.setPopup(popup);
popup.addTo(map);
popup.setLngLat(marker.getLngLat());
});
el.addEventListener("mouseleave", () => popup.remove());
或直接绑定:
marker.setPopup(popup);
marker.togglePopup(); // 点 Marker 切换
五、批量 Marker 事件委托
如果 DOM 都有统一父元素,可用事件委托:
map.getCanvasContainer().addEventListener("click", e => {
const el = e.target.closest(".my-marker"); // DOM 元素,非 Marker 实例
if (el) {
const id = el.dataset.id;
console.log("点击 marker", id);
}
});
Marker 元素就挂在
map.getCanvasContainer()下(与 canvas 同级),所以委托到 canvasContainer 有效;用closest过滤出 Marker 节点即可。
六、拖拽 + 点击防抖
let isDragging = false;
marker.on("dragstart", () => { isDragging = true; });
marker.on("dragend", () => setTimeout(() => { isDragging = false; }, 50));
marker.getElement().addEventListener("click", e => {
if (isDragging) return;
alert("click");
});
七、autoAdd / autoRemove(视野裁剪事件)
const m = vjmap.createMarker({ removeWhenNoInMapView: true }) // 扩展字段须走 createMarker(或 setRemoveWhenNoInMapView)
.setLngLat(lngLat);
m.on("autoAdd", () => console.log("进入视野"));
m.on("autoRemove", () => console.log("离开视野"));
m.addTo(map); // addTo 返回 undefined,不能在其后接 .on(...)
场景:把进入视野的 Marker 数量写入统计 / UI 显示。
八、监听缩放级别触发的 show/hide
map.on("zoomend", () => {
const z = map.getZoom();
marker.getElement().style.opacity = z > 15 ? 1 : 0.3;
});
九、配合地图 click 做反向 hitTest
map.on("click", e => {
// 计算最近的 Marker(注意:Marker 没有 id 属性,自己维护 id→marker 映射)
let nearest = null, nearestId = null, minDist = Infinity;
for (const [id, m] of markerMap) {
const d = distance(e.lngLat, m.getLngLat());
if (d < minDist) { minDist = d; nearest = m; nearestId = id; }
}
if (minDist < 100) alert("最近的 Marker id=" + nearestId);
});
十、常见坑
- click 未触发 → CSS
pointer-events: none让 Marker 不接收事件;改为auto。 - click 同时触发了 map click →
e.stopPropagation()在 Marker element 的 click 里。 - hover 闪烁 → 检查
closeOnMove:默认false;若显式开了,地图 pan/zoom 时会关 popup(不是鼠标移动),hover 联动场景保持默认即可。 - autoRemove 不丢监听:autoRemove 只是把同一 DOM 节点从文档摘除、autoAdd 再插回,自己
addEventListener的监听和marker.on的订阅都还在,无需重绑(注意:autoRemove 会顺带关闭绑定的 popup)。 - Vue/React 里把 click handler 写成闭包:每次渲染都重建函数,建议用
useCallback/ 稳定引用。