DomEvent.DoubleTap
# 概述
DomEvent.DoubleTap
模块是Leaflet中用于模拟双击(dbclick
)事件的模块,主要解决移动端浏览器对双击事件支持不完善或延迟的问题,同时避免与标签(<label>
)关联的表单元素误触发。
# 源码分析
# 源码实现如下
DomEvent.DoubleTap
的源码实现如下:
function makeDblclick(event) {
var newEvent = {},
prop,
i;
// 复制原始事件的所有属性(包括方法)
for (i in event) {
prop = event[i];
newEvent[i] = prop && prop.bind ? prop.bind(event) : prop;
}
event = newEvent;
// 修改事件类型为dbclick,并标记为模拟事件
newEvent.type = "dblclick";
newEvent.detail = 2;//表示双击
newEvent.isTrusted = false;//非浏览器原生触发
newEvent._simulated = true; //Leaflet内部标记
return newEvent;
}
var delay = 200;
export function addDoubleTapListener(obj, handler) {
// 直接监听原生dbclick事件(桌面浏览器)
obj.addEventListener("dblclick", handler);
var last = 0,
detail;
function simDblclick(e) {
//忽略非单次点击事件
if (e.detail !== 1) {
detail = e.detail;
return;
}
// 过滤鼠标事件,仅处理触控或笔触
if (
e.pointerType === "mouse" ||
(e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)
) {
return;
}
// 检查事件路径,避免label元素误触发
var path = DomEvent.getPropagationPath(e);
if (
path.some(function (el) {
return el instanceof HTMLLabelElement && el.attributes.for;
}) &&
!path.some(function (el) {
return (
el instanceof HTMLInputElement || el instanceof HTMLSelectElement
);
})
) {
return;
}
// 计算事件间隔,模拟双击
var now = Date.now();
if (now - last <= delay) {
detail++;
if (detail === 2) {
handler(makeDblclick(e)); //触发自定义dbclick事件
}
} else {
detail = 1;
}
last = now;
}
// 监听click事件,通过时间间隔判断双击
obj.addEventListener("click", simDblclick);
return {
dblclick: handler,
simDblclick: simDblclick,
};
}
export function removeDoubleTapListener(obj, handlers) {
obj.removeEventListener("dblclick", handlers.dblclick);
obj.removeEventListener("click", handlers.simDblclick);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 源码详解
makeDbclick(event)
函数:构造模拟的双击事件:
- 目的:克隆原始事件并转换为自定义的
dblclick
事件,用于模拟双击行为。 - 关键点:
- 属性复制:遍历原始事件属性,若属性是方法(如
preventDefault
),则绑定到原始事件上下文。 - 标记为非可信事件:
isTrusted: false
表示此事件由脚本生成,而非用户直接操作触发。
- 属性复制:遍历原始事件属性,若属性是方法(如
addDoubleTapListener(obj, handler)
函数:添加双击监听:
- 核心逻辑:
双重监听:
- 直接监听原生 dblclick(桌面浏览器有效)。
- 通过 click 事件模拟移动端双击(simDblclick)。
过滤非触控事件:
pointerType === 'mouse'
:排除鼠标操作。sourceCapabilities.firesTouchEvents
:仅处理触控或笔触(Chrome 特性)。
避免
<label>
误触发:- 若事件路径中存在关联
<label for>
但无对应表单元素(如<input>
),则忽略点击。
- 若事件路径中存在关联
时间间隔判断:
- 两次
click
间隔小于200ms
视为双击,触发自定义dblclick
事件。
- 两次
removeDoubleTapListener(obj, handlers)
函数:移除双击监听
目的:移除之前通过
addDoubleTapListener
添加的dblclick
和simDblclick
事件监听器。关键点:需传入之前返回的
handles
对象,确保移除正确的监听函数。
# 关键设计思想
移动端兼容性: 移动端浏览器通常不直接支持
dbclick
事件,通过快速连续点击(click
)模拟双击行为避免冲突:
- 过滤
<label>
元素点击,防止其关联的表单元素(如复选框)被误操作。 - 区分触控和鼠标事件,避免重复处理。
- 过滤
性能优化:
- 使用
200ms
间隔判断双击,平衡响应速度和误触概率。 - 通过
DomEvent.getPropagationPath(e)
获取事件传播路径,精确控制事件逻辑。
- 使用
# 使用场景
- 地图双击缩放:用户快速双击地图时,触发放大地图的操作。
- 移动端手势支持:在触控设备上实现与桌面浏览器一致的双击交互。
# 潜在问题与注意事项
事件可信度: 模拟的
dblclick
事件isTrusted: false
,某些安全策略可能限制其行为(如阻止默认动作)。时间间隔敏感性:
200ms
的间隔可能导致部分用户操作未被识别为双击,需根据实际需求调整。
- 浏览器兼容性:
sourceCapabilities
是 Chrome 特有属性,其他浏览器可能需额外兼容处理。DomEvent.getPropagationPath
是 Leaflet 内部方法,用于获取事件传播路径(等价于e.composedPath()
)。
# 总结
- 核心功能:通过监听快速连续的
click
事件模拟dblclick
,解决移动端兼容性问题。 - 设计亮点:
- 双重事件监听(原生 + 模拟)兼顾不同环境。
- 路径过滤避免
- 严格区分触控与鼠标事件。
- 适用场景:需要跨平台支持双击交互的 Web 应用(尤其是地图库)。
编辑 (opens new window)
上次更新: 2025/03/21, 01:49:37