You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Leaflet地图右键菜单点击事件重复触发问题求助

问题原因

每次右键触发contextmenu事件时,你都会给copyCoordinates和copyMarker按钮重复添加点击事件监听器。第一次右键后按钮绑定1个监听器,第二次右键又新增1个,点击时所有绑定的监听器都会执行,因此出现执行次数逐次递增的情况。

解决方案

核心是避免重复绑定事件,以下是两种可行方案:

方案1:将事件绑定移到contextmenu外部,通过变量存储当前坐标

把按钮的点击事件绑定放在地图初始化阶段(仅执行一次),contextmenu事件只负责显示菜单和更新当前右键的坐标,点击按钮时读取该坐标执行操作:

// 存储当前右键的坐标与地图缩放级别
let currentContextLatLng = null;
let currentContextZoom = null;

// 初始化时绑定按钮点击事件(仅执行一次)
document.getElementById('copyCoordinates').addEventListener('click', function() {
    if (!currentContextLatLng) return;
    
    const lat = currentContextLatLng.lat;
    const lng = currentContextLatLng.lng;
    const zoom = currentContextZoom;
    const params = `lat=${lat}&lon=${lng}&zoom=${zoom}`;
    const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?${params}`;
    navigator.clipboard.writeText(newUrl);
    toastr.success('Copied coordinates to clipboard!', {timeOut: 5000});
    document.getElementById('context-menu').style.display = 'none';
});

document.getElementById('copyMarker').addEventListener('click', function() {
    if (!currentContextLatLng) return;
    
    const lat = Math.floor(currentContextLatLng.lat);
    const lng = Math.floor(currentContextLatLng.lng);
    const formatted = `"lat": ${lat},\n  "lng": ${lng},`;
    navigator.clipboard.writeText(formatted);
    toastr.success('Copied coordinates to clipboard!', {timeOut: 5000});
    document.getElementById('context-menu').style.display = 'none';
});

// contextmenu事件仅负责显示菜单和更新坐标
map.on('contextmenu', function(e) {
    const menu = document.getElementById('context-menu');
    menu.style.display = 'block';
    menu.style.left = `${e.originalEvent.x}px`;
    menu.style.top = `${e.originalEvent.y}px`;
    
    currentContextLatLng = e.latlng;
    currentContextZoom = map.getZoom();
});

方案2:添加新监听器前先移除旧监听器(使用命名函数)

如果必须在contextmenu事件内绑定事件,需先移除之前的监听器,再添加新的:

// 提前定义命名函数
function copyCoordinates() {
    if (!this.currentLatLng) return;
    
    const lat = this.currentLatLng.lat;
    const lng = this.currentLatLng.lng;
    const zoom = this.currentZoom;
    const params = `lat=${lat}&lon=${lng}&zoom=${zoom}`;
    const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?${params}`;
    navigator.clipboard.writeText(newUrl);
    toastr.success('Copied coordinates to clipboard!', {timeOut: 5000});
    document.getElementById('context-menu').style.display = 'none';
}

function copyCoordinatesFormatted() {
    if (!this.currentLatLng) return;
    
    const lat = Math.floor(this.currentLatLng.lat);
    const lng = Math.floor(this.currentLatLng.lng);
    const formatted = `"lat": ${lat},\n  "lng": ${lng},`;
    navigator.clipboard.writeText(formatted);
    toastr.success('Copied coordinates to clipboard!', {timeOut: 5000});
    document.getElementById('context-menu').style.display = 'none';
}

map.on('contextmenu', function(e) {
    const menu = document.getElementById('context-menu');
    menu.style.display = 'block';
    menu.style.left = `${e.originalEvent.x}px`;
    menu.style.top = `${e.originalEvent.y}px`;
    
    const copyBtn = document.getElementById('copyCoordinates');
    const markerBtn = document.getElementById('copyMarker');
    
    // 先移除旧监听器,再添加新的
    copyBtn.removeEventListener('click', copyCoordinates);
    markerBtn.removeEventListener('click', copyCoordinatesFormatted);
    
    // 给函数绑定当前坐标和缩放级别
    const boundCopy = copyCoordinates.bind({
        currentLatLng: e.latlng,
        currentZoom: map.getZoom()
    });
    const boundCopyFormatted = copyCoordinatesFormatted.bind({
        currentLatLng: e.latlng,
        currentZoom: map.getZoom()
    });
    
    copyBtn.addEventListener('click', boundCopy);
    markerBtn.addEventListener('click', boundCopyFormatted);
});

额外优化建议

  • 右键点击页面其他区域时自动隐藏菜单,可添加全局点击监听:
    document.addEventListener('click', function(e) {
        const menu = document.getElementById('context-menu');
        if (!menu.contains(e.target)) {
            menu.style.display = 'none';
        }
    });
    
  • 使用模板字符串(${变量})替代字符串拼接,代码更简洁易读。

内容的提问来源于stack exchange,提问作者LeJamal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:45:51