当前位置: 首页 > news >正文

JavaScript中左键单击(click)与双击(dblclick)事件的关系解析地图操作避坑

文章目录

    • JavaScript中左键单击(click)与双击(dblclick)事件的关系解析
      • 一、概念解析
        • 单击事件(click)
        • 双击事件(dblclick)
      • 二、关键区别
      • 三、实例演示
        • 按钮层面
        • 地图交互层面 - Cesium
        • 地图交互层面 - Leaflet
      • 四、关系与影响分析
        • 协作关系
        • 冲突本质
        • 多层面影响
      • 五、最佳实践建议
        • 明确交互场景
        • 防抖函数封装
        • 事件冒泡控制
        • 语义化提示
    • 重点关注,地图操作的单机与双击影响
      • Cesium 中避免单击和双击事件相互影响
        • 实现思路
        • 代码示例
        • 代码解释
      • Leaflet 中避免单击和双击事件相互影响
        • 实现思路
        • 代码示例
        • 代码解释

JavaScript中左键单击(click)与双击(dblclick)事件的关系解析

一、概念解析

单击事件(click)

当用户完成一次完整的鼠标左键按键动作,即 mousedown(鼠标按下)紧接着 mouseup(鼠标松开)时,就会触发 click 事件。它是一种基础的交互事件,在网页开发中极为常见,常用于触发简单操作,例如按钮提交表单、链接跳转等。

双击事件(dblclick)

在短时间(通常是 300 毫秒)内连续触发两次单击事件后,浏览器会自动抑制第二次单击的默认行为,转而触发 dblclick 事件。它依赖于单击事件,常用于实现快速操作,像文本编辑、图片缩放等。

二、关键区别

特性单击事件双击事件
触发条件单次点击300ms 内两次点击
事件独立性独立触发依赖单击事件,浏览器自动抑制第二次单击
默认行为触发绑定逻辑可能覆盖单击逻辑(需防抖处理)

三、实例演示

按钮层面
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Button Click and DblClick</title>
</head>

<body>
    <button id="btn">点击我</button>
    <script>
        const btn = document.getElementById('btn');
        btn.addEventListener('click', () => console.log('单击事件触发'));
        btn.addEventListener('dblclick', () => console.log('双击事件触发'));
    </script>
</body>

</html>

在这个例子中,当用户单击按钮时,控制台会输出“单击事件触发”;当用户在 300 毫秒内双击按钮时,控制台会依次输出“单击事件触发”、“单击事件触发”、“双击事件触发”。不过在实际开发里,第二次单击事件可能会因代码逻辑残留而触发。

地图交互层面 - Cesium
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>Cesium Click and DblClick</title>
    <script src="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Cesium.js"></script>
    <link href="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        html,
        body,
        #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="cesiumContainer"></div>
    <script>
        const viewer = new Cesium.Viewer('cesiumContainer');
        const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

        // 处理点击事件
        handler.setInputAction(function (movement) {
            const cartesian = viewer.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
            if (cartesian) {
                const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                const longitude = Cesium.Math.toDegrees(cartographic.longitude);
                const latitude = Cesium.Math.toDegrees(cartographic.latitude);
                console.log(`单击位置:经度 ${longitude}, 纬度 ${latitude}`);
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        // 处理双击事件
        handler.setInputAction(function (movement) {
            viewer.camera.zoomIn(100000);
            console.log('双击事件触发,地图放大');
        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    </script>
</body>

</html>    

在这个 Cesium 示例中,点击地图会输出点击位置的经纬度,双击地图则会使地图放大。

地图交互层面 - Leaflet
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Leaflet Click and DblClick</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <style>
        #map {
            width: 100%;
            height: 400px;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        const map = L.map('map').setView([51.505, -0.09], 13);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
            maxZoom: 18
        }).addTo(map);

        // 处理点击事件
        map.on('click', function (e) {
            console.log(`单击位置:经度 ${e.latlng.lng}, 纬度 ${e.latlng.lat}`);
        });

        // 处理双击事件
        map.on('dblclick', function (e) {
            map.setZoom(map.getZoom() + 1);
            console.log('双击事件触发,地图放大');
        });
    </script>
</body>

</html>    

在这个 Leaflet 示例中,点击地图会输出点击位置的经纬度,双击地图会使地图放大一级。

四、关系与影响分析

协作关系

双击事件本质上是对快速连续单击操作的语义化封装,它简化了“双击”这一常见交互的实现。在很多场景下,用户可以通过双击来执行更高效的操作,而开发人员也能更方便地实现相应功能。

冲突本质
  • 时间窗口冲突:双击的 300 毫秒判定窗口与单击逻辑可能会重叠,这就可能导致在双击时同时触发单击和双击的逻辑。
  • 事件冒泡冲突:子元素的双击事件可能会冒泡触发父元素的单击逻辑,需要通过 e.stopPropagation() 来控制。
多层面影响
层面影响分析
用户体验双击操作要求用户进行精准操作,一旦误触就可能影响用户的满意度。在不同的设备和场景下,需要谨慎选择交互方式,例如在移动端要慎用双击操作。
功能设计双击操作适合用于高频操作,如选中文字;而单击操作则更适合基础交互。在进行功能设计时,要避免功能重叠,确保用户操作的明确性。
代码维护如果没有处理好单击和双击事件的冲突,可能会导致代码逻辑混乱。建议封装通用的防抖函数来统一管理单击和双击逻辑,提高代码的可维护性。

五、最佳实践建议

明确交互场景

优先为单一操作选择一种交互方式,例如“提交”操作使用单击,“编辑”操作使用双击,避免用户产生混淆。

防抖函数封装
function debounce(func, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => func.apply(this, args), delay);
    };
}

// 使用示例
const btn = document.getElementById('btn');
btn.addEventListener('click', debounce(() => {
    console.log('单击逻辑');
}, 300));
事件冒泡控制
const childElement = document.getElementById('child');
childElement.addEventListener('dblclick', (e) => {
    e.stopPropagation(); // 阻止双击事件冒泡触发父元素单击逻辑
    console.log('子元素双击事件触发');
});
语义化提示

通过 UI 提示,如双击图标、文字说明等,引导用户了解交互方式,降低用户的学习成本。

综上所述,单击和双击事件在 JavaScript 中既相互协作又存在冲突。通过合理运用防抖机制、控制事件冒泡以及明确场景设计,可以实现两者的和谐共存,提升用户的交互体验。

重点关注,地图操作的单机与双击影响

Cesium 中避免单击和双击事件相互影响

实现思路

在 Cesium 里,借助 ScreenSpaceEventHandler 监听鼠标的单击和双击事件。为了避免两者相互影响,可采用防抖机制。当触发单击事件时,设置一个定时器,若在特定时间(如 300ms)内没有再次触发单击事件,就执行单击逻辑;若在该时间内触发了双击事件,则取消定时器并执行双击逻辑。

代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>Cesium Click and DblClick Avoid Interference</title>
    <script src="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Cesium.js"></script>
    <link href="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        html,
        body,
        #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="cesiumContainer"></div>
    <script>
        // 初始化 Cesium Viewer
        const viewer = new Cesium.Viewer('cesiumContainer');
        const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
        let clickTimer = null;

        // 处理单击事件
        handler.setInputAction(function (movement) {
            clearTimeout(clickTimer);
            clickTimer = setTimeout(() => {
                const cartesian = viewer.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
                if (cartesian) {
                    const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                    const longitude = Cesium.Math.toDegrees(cartographic.longitude);
                    const latitude = Cesium.Math.toDegrees(cartographic.latitude);
                    console.log(`单击位置:经度 ${longitude}, 纬度 ${latitude}`);
                }
            }, 300);
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        // 处理双击事件
        handler.setInputAction(function (movement) {
            clearTimeout(clickTimer);
            viewer.camera.zoomIn(100000);
            console.log('双击事件触发,地图放大');
        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    </script>
</body>

</html>    
代码解释
  • 当触发单击事件时,先清除之前设置的定时器,接着重新设置一个 300ms 的定时器。若 300ms 内没有再次触发单击事件,就执行单击逻辑,输出单击位置的经纬度。
  • 当触发双击事件时,清除定时器,防止执行单击逻辑,然后执行双击逻辑,让地图放大。

Leaflet 中避免单击和双击事件相互影响

实现思路

在 Leaflet 中,通过监听地图的 clickdblclick 事件来处理。同样运用防抖机制,当触发 click 事件时,设置一个定时器,若在 300ms 内没有再次触发 click 事件,就执行单击逻辑;若触发了 dblclick 事件,则取消定时器并执行双击逻辑。

代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Leaflet Click and DblClick Avoid Interference</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <style>
        #map {
            width: 100%;
            height: 400px;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        // 初始化地图
        const map = L.map('map').setView([51.505, -0.09], 13);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
            maxZoom: 18
        }).addTo(map);

        let clickTimer = null;

        // 处理单击事件
        map.on('click', function (e) {
            clearTimeout(clickTimer);
            clickTimer = setTimeout(() => {
                console.log(`单击位置:经度 ${e.latlng.lng}, 纬度 ${e.latlng.lat}`);
            }, 300);
        });

        // 处理双击事件
        map.on('dblclick', function (e) {
            clearTimeout(clickTimer);
            map.setZoom(map.getZoom() + 1);
            console.log('双击事件触发,地图放大');
        });
    </script>
</body>

</html>    
代码解释
  • 当触发 click 事件时,清除之前的定时器,重新设置一个 300ms 的定时器。若 300ms 内没有再次触发 click 事件,就执行单击逻辑,输出单击位置的经纬度。
  • 当触发 dblclick 事件时,清除定时器,避免执行单击逻辑,然后执行双击逻辑,使地图放大一级。
http://www.dtcms.com/a/112672.html

相关文章:

  • vue项目data functions should return an object
  • Linux的 `/proc` 目录 笔记250404
  • 【kubernetes】BusyBox
  • 试用thymeleaf引入vue-element-admin(一)
  • 工业领域网络安全技术发展路径洞察报告发布 | FreeBuf咨询
  • HTTP Content-Type
  • 【CSP】202403-1词频统计
  • Haskell语言的云安全
  • Linux文件处理三剑客详解-grep,sed,awk
  • 如何使用 Puppeteer 解决 reCAPTCHA 并提高成功率
  • TDengine 快速上手:安装部署与基础 SQL 实践(一)
  • TypeScript语言的操作系统原理
  • FPGA实验记录
  • VSCode远程连接
  • WebSocket 详解:构建一个复杂的实时聊天应用
  • acme.sh管理 SSL/TLS 证书
  • 使用PyTorch训练VGG11模型:Fashion-MNIST图像分类实战
  • 0302useState-hooks-react-仿低代码平台项目
  • AingDesk是一款简单好用的AI助手,支持知识库、模型 API、分享、联网搜索、智能体,它正在迅速发展和改进
  • 电机控制学习路线
  • SpringCloud(25)——Stream介绍
  • 4.4 力扣|59螺旋矩阵II
  • AI与Python在地球科学多源数据交叉融合中的前沿
  • MCP(模型上下文协议)入门指南:用Web开发的视角理解下一代AI引擎
  • 集合与容器:List、HashMap(II)
  • 用 Docker Compose 与 Nginx 反向代理部署 Vikunja 待办事项管理系统
  • Leetcode 135 -- 贪心 | 拓扑排序
  • 【多线程-第四天-自己模拟SDWebImage的下载图片功能-下载操作管理类 Objective-C语言】
  • 如何排查、定位 SQL 慢查询及其优化策略
  • 16进制在蓝牙传输中的应用