> **本文回答**：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`**） |

```js
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 监听：

```js
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 传递经纬度

```js
const el = marker.getElement();
el.addEventListener("click", () => {
  const ll = marker.getLngLat();
  alert(`点击 (${ll.lng}, ${ll.lat})`);
});
```

## 四、hover 联动 Popup

```js
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());
```

或直接绑定：

```js
marker.setPopup(popup);
marker.togglePopup();   // 点 Marker 切换
```

## 五、批量 Marker 事件委托

如果 DOM 都有统一父元素，可用事件委托：

```js
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 节点即可。

## 六、拖拽 + 点击防抖

```js
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（视野裁剪事件）

```js
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

```js
map.on("zoomend", () => {
  const z = map.getZoom();
  marker.getElement().style.opacity = z > 15 ? 1 : 0.3;
});
```

## 九、配合地图 click 做反向 hitTest

```js
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);
});
```

## 十、常见坑

1. **click 未触发** → CSS `pointer-events: none` 让 Marker 不接收事件；改为 `auto`。
2. **click 同时触发了 map click** → `e.stopPropagation()` 在 Marker element 的 click 里。
3. **hover 闪烁** → 检查 `closeOnMove`：默认 `false`；若显式开了，**地图** pan/zoom 时会关 popup（不是鼠标移动），hover 联动场景保持默认即可。
4. **autoRemove 不丢监听**：autoRemove 只是把同一 DOM 节点从文档摘除、autoAdd 再插回，自己 `addEventListener` 的监听和 `marker.on` 的订阅都还在，无需重绑（注意：autoRemove 会顺带关闭绑定的 popup）。
5. **Vue/React 里把 click handler 写成闭包**：每次渲染都重建函数，建议用 `useCallback` / 稳定引用。
