DomUtil
# 概述
DomUtil
模块是Leaflet中用于处理 DOM 操作和事件的核心工具函数集合,这些工具函数主要用于处理 DOM 元素的样式、位置、变换、事件等操作。
# 源码分析
# 源码实现如下
DomUtil
源码实现如下:
export var TRANSFORM = testProp([
"transform",
"webkitTransform",
"OTransform",
"MozTransform",
"msTransform",
]);
export var TRANSITION = testProp([
"webkitTransition",
"transition",
"OTransition",
"MozTransition",
"msTransition",
]);
export var TRANSITION_END =
TRANSITION === "webkitTransition" || TRANSITION === "OTransition"
? TRANSITION + "End"
: "transitionend";
export function get(id) {
return typeof id === "string" ? document.getElementById(id) : id;
}
export function getStyle(el, style) {
var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
if ((!value || value === "auto") && document.defaultView) {
var css = document.defaultView.getComputedStyle(el, null);
value = css ? css[style] : null;
}
return value === "auto" ? null : value;
}
export function create(tagName, className, container) {
var el = document.createElement(tagName);
el.className = className || "";
if (container) {
container.appendChild(el);
}
return el;
}
export function remove(el) {
var parent = el.parentNode;
if (parent) {
parent.removeChild(el);
}
}
export function empty(el) {
while (el.firstChild) {
el.removeChild(el.firstChild);
}
}
export function toFront(el) {
var parent = el.parentNode;
if (parent && parent.lastChild !== el) {
parent.appendChild(el);
}
}
export function toBack(el) {
var parent = el.parentNode;
if (parent && parent.firstChild !== el) {
parent.insertBefore(el, parent.firstChild);
}
}
export function hasClass(el, name) {
if (el.classList !== undefined) {
return el.classList.contains(name);
}
var className = getClass(el);
return (
className.length > 0 &&
new RegExp("(^|\\s)" + name + "(\\s|$)").test(className)
);
}
export function addClass(el, name) {
if (el.classList !== undefined) {
var classes = Util.splitWords(name);
for (var i = 0, len = classes.length; i < len; i++) {
el.classList.add(classes[i]);
}
} else if (!hasClass(el, name)) {
var className = getClass(el);
setClass(el, (className ? className + " " : "") + name);
}
}
export function removeClass(el, name) {
if (el.classList !== undefined) {
el.classList.remove(name);
} else {
setClass(
el,
Util.trim((" " + getClass(el) + " ").replace(" " + name + " ", " "))
);
}
}
export function setClass(el, name) {
if (el.className.baseVal === undefined) {
el.className = name;
} else {
// in case of SVG element
el.className.baseVal = name;
}
}
export function getClass(el) {
if (el.correspondingElement) {
el = el.correspondingElement;
}
return el.className.baseVal === undefined
? el.className
: el.className.baseVal;
}
export function setOpacity(el, value) {
if ("opacity" in el.style) {
el.style.opacity = value;
} else if ("filter" in el.style) {
_setOpacityIE(el, value);
}
}
function _setOpacityIE(el, value) {
var filter = false,
filterName = "DXImageTransform.Microsoft.Alpha";
// filters collection throws an error if we try to retrieve a filter that doesn't exist
try {
filter = el.filters.item(filterName);
} catch (e) {
if (value === 1) {
return;
}
}
value = Math.round(value * 100);
if (filter) {
filter.Enabled = value !== 100;
filter.Opacity = value;
} else {
el.style.filter += " progid:" + filterName + "(opacity=" + value + ")";
}
}
export function testProp(props) {
var style = document.documentElement.style;
for (var i = 0; i < props.length; i++) {
if (props[i] in style) {
return props[i];
}
}
return false;
}
export function setTransform(el, offset, scale) {
var pos = offset || new Point(0, 0);
el.style[TRANSFORM] =
(Browser.ie3d
? "translate(" + pos.x + "px," + pos.y + "px)"
: "translate3d(" + pos.x + "px," + pos.y + "px,0)") +
(scale ? " scale(" + scale + ")" : "");
}
export function setPosition(el, point) {
el._leaflet_pos = point;
if (Browser.any3d) {
setTransform(el, point);
} else {
el.style.left = point.x + "px";
el.style.top = point.y + "px";
}
}
export function getPosition(el) {
return el._leaflet_pos || new Point(0, 0);
}
export var disableTextSelection;
export var enableTextSelection;
var _userSelect;
if ("onselectstart" in document) {
disableTextSelection = function () {
DomEvent.on(window, "selectstart", DomEvent.preventDefault);
};
enableTextSelection = function () {
DomEvent.off(window, "selectstart", DomEvent.preventDefault);
};
} else {
var userSelectProperty = testProp([
"userSelect",
"WebkitUserSelect",
"OUserSelect",
"MozUserSelect",
"msUserSelect",
]);
disableTextSelection = function () {
if (userSelectProperty) {
var style = document.documentElement.style;
_userSelect = style[userSelectProperty];
style[userSelectProperty] = "none";
}
};
enableTextSelection = function () {
if (userSelectProperty) {
document.documentElement.style[userSelectProperty] = _userSelect;
_userSelect = undefined;
}
};
}
export function disableImageDrag() {
DomEvent.on(window, "dragstart", DomEvent.preventDefault);
}
export function enableImageDrag() {
DomEvent.off(window, "dragstart", DomEvent.preventDefault);
}
var _outlineElement, _outlineStyle;
export function preventOutline(element) {
while (element.tabIndex === -1) {
element = element.parentNode;
}
if (!element.style) {
return;
}
restoreOutline();
_outlineElement = element;
_outlineStyle = element.style.outlineStyle;
element.style.outlineStyle = "none";
DomEvent.on(window, "keydown", restoreOutline);
}
export function restoreOutline() {
if (!_outlineElement) {
return;
}
_outlineElement.style.outlineStyle = _outlineStyle;
_outlineElement = undefined;
_outlineStyle = undefined;
DomEvent.off(window, "keydown", restoreOutline);
}
export function getSizedParentNode(element) {
do {
element = element.parentNode;
} while (
(!element.offsetWidth || !element.offsetHeight) &&
element !== document.body
);
return element;
}
export function getScale(element) {
var rect = element.getBoundingClientRect(); // Read-only in old browsers.
return {
x: rect.width / element.offsetWidth || 1,
y: rect.height / element.offsetHeight || 1,
boundingClientRect: rect,
};
}
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# 主要工具函数介绍
TRANSFORM
和TRANSITION
- 作用:检测浏览器支持的 CSS 变换(
transform
)和过渡(transition
)属性 - 实现:通过
testProp
函数遍历一组可能的属性名称,返回浏览器支持的第一个属性 - 意义:跨浏览器兼容性处理,确保在不同浏览器中正确应用 CSS 变换和过渡
TRANSITION_END
- 作用:确定浏览器支持的
transitionend
事件名称。 - 实现:根据
TRANSITION
属性的值,返回对应的事件名称(如webkitTransitionEnd
或transitionend
)。 - 意义:确保在 CSS 过渡结束时正确触发事件
- DOM 操作工具函数
get(id)
- 作用:通过
ID
获取 DOM 元素。 - 实现:如果传入的是字符串,调用
document.getElementById
;否则直接返回传入的值。
- 作用:通过
getStyle(el, style)
- 作用:获取元素的样式值。
- 实现:优先从
el.style
或el.currentStyle
中获取样式值,如果未找到或值为auto
,则通过getComputedStyle
获取。
create(tagName, className, container)
- 作用:创建并返回一个 DOM 元素。
- 实现:使用
document.createElement
创建元素,并可选地将其附加到指定容器中。
remove(el)
- 作用:从 DOM 中移除元素。
- 实现:调用
parentNode.removeChild
。
empty(el)
- 作用:清空元素的所有子节点。
- 实现:循环移除
el.firstChild
。
toFront(el)
和toBack(el)
- 作用:将元素移动到其父容器的顶部或底部。
- 实现:通过
appendChild
或insertBefore
调整元素的位置。
4. 类名操作工具函数
hasClass(el, name)
- 作用:检查元素是否包含指定的类名。
- 实现:优先使用
el.classList.contains
,否则通过正则表达式匹配。
addClass(el, name)
和removeClass(el, name)
- 作用:添加或移除元素的类名。
- 实现:优先使用
el.classList
,否则通过字符串操作修改className
。
setClass(el, name)
和getClass(el)
- 作用:设置或获取元素的类名。
- 实现:处理普通元素和 SVG 元素的兼容性。
5. 透明度操作工具函数
setOpacity(el, value)
- 作用:设置元素的透明度。
- 实现:优先使用
opacity
属性,否则通过 IE 的filter
属性实现。
setOpacityIE(el, value)
- 作用:在 IE 中设置透明度。
- 实现:通过
DXImageTransform.Microsoft.Alpha
滤镜实现。
- 变换和位置操作工具函数
testProp(props)
- 作用:检测浏览器支持的 CSS 属性。
- 实现:遍历传入的属性列表,返回第一个支持的属性。
setTransform(el, offset, scale)
- 作用:设置元素的变换(平移和缩放)。
- 实现:根据浏览器支持,使用
translate
或translate3d
。
setPosition(el, point)
和getPosition(el)
- 作用:设置或获取元素的位置。
- 实现:优先使用
transform
,否则通过left
和top
实现。
7. 文本选择和图像拖拽工具函数
disableTextSelection()
和enableTextSelection()
- 作用:禁用或启用文本选择。
- 实现:通过
selectstart
事件或userSelect
属性实现。
disableImageDrag()
和enableImageDrag()
- 作用:禁用或启用图像拖拽。
- 实现:通过
dragstart
事件实现。
- 轮廓线操作工具函数
preventOutline(element)
和restoreOutline()
- 作用:禁用或恢复元素的轮廓线。
- 实现:通过修改
outlineStyle
实现。
- 其他工具函数
getSizedParentNode(element)
- 作用:获取具有实际尺寸的父节点。
- 实现:循环查找父节点,直到找到具有
offsetWidth
和offsetHeight
的节点。
getScale(element)
- 作用:获取元素的缩放比例。
- 实现:通过
getBoundingClientRect
和offsetWidth/Height
计算。
# 总结
这段代码是 Leaflet 中用于处理 DOM 操作和事件的工具函数集合,涵盖了元素创建、删除、样式操作、变换、事件处理等功能。它的设计目标是:
- 跨浏览器兼容性:通过检测和适配不同浏览器的属性和事件名称。
- 轻量高效:使用原生 DOM API,避免不必要的依赖。
- 模块化:每个函数功能独立,易于复用和扩展。
这些工具函数为 Leaflet 的核心功能(如地图渲染、交互、动画等)提供了基础支持。
编辑 (opens new window)
上次更新: 2025/03/20, 00:55:00