常见的事件封装
2020/7/20 event
# 获取键盘事件的键值
function getKeyCode(e) {
e = e ? e : (window.event ? window.event : "")
return e.keyCode ? e.keyCode : e.which
}
1
2
3
4
2
3
4
# onmousewheel 兼容
// 兼容onmousewheel
function addMouseWheelEvent(element, func) {
if (typeof element.onmousewheel == "object") {
element.onmousewheel = function (ev) {
ev = ev || window.event;
//console.log(ev.wheelDelta);
func();
};
} else {
// 兼容 firefox
element.addEventListener("DOMMouseScroll", function (ev) {
ev = ev || window.event;
//console.log('ev.detail : '+ev.detail);
func();
}, false);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 禁止选中
// 禁止选中
function disabledSel() {
if (document.all) {
document.onselectstart = function () { return false; }; //for ie
} else {
document.onmousedown = function () { return false; };
document.onmouseup = function () { return true; };
}
document.onselectstart = new Function('event.returnValue=false;');
if (window.getSelector) {
var selection = window.getSelection();
selection.removeAllRanges();
} else if (document.selection && document.selection.empty) {
document.selection.empty();
// document.selection.clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# requestAnimationFrame
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz', 'o', 'ms'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
// name has changed in Webkit
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
}());
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
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
# DOMContentLoaded
// Javascript封装DOMContentLoaded事件
function ready(callback) {
// 目前Mozilla、Opera和webkit 525+内核支持DOMContentLoaded事件
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', function () {
document.removeEventListener('DOMContentLoaded', arguments.callee, false);
callback && callback();
}, false);
}
// 如果IE
else if (document.attachEvent) {
// 确保当页面是在iframe中加载时,事件依旧会被安全触发
document.attachEvent('onreadystatechange', function () {
if (document.readyState == 'complete') {
document.detachEvent('onreadystatechange', arguments.callee);
callback && callback();
}
});
// 如果是IE且页面不在iframe中时,轮询调用doScroll 方法检测DOM是否加载完毕
if (document.documentElement.doScroll && typeof window.frameElement === "undefined") {
try {
document.documentElement.doScroll('left');
}
catch (error) {
return setTimeout(arguments.callee, 20);
};
callback && callback();
}
}
};
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
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
# stopPropagation(阻止冒泡)
const stopPropagation = function (e) {
e = e || window.event;
if (document.all) { //只有ie识别
e.cancelBubble = true;
} else {
e.stopPropagation();
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# preventDefault(阻止默认事件)
const stopDefault = function (e) {
e = e || window.event;
if (document.all) {
window.event.returnValue = false;
} else {
event.preventDefault();
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 事件对象Event
const event = event || window.event;
1
# 事件目标event.target
const eventTarget = event.target || event.srcElement;
1
# addEventListener
const addEvent = function (element, type, handler) {
if (element.addEventListener) { //DOM2级
element.addEventListener(type, handler, false);
} else if (element.attachEvent) { //DOM1级
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler; //DOM0级
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# removeEventListener
const removeEvent = function (element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# dispatchDOMEvent
function dispatchDOMEvent(el, payload, eventInit) {
let event;
if (void 0 !== Event) {
event = new Event(type, eventInit);
} else {
event = document.createEvent("HTMLEvents");
event.initEvent(type, eventInit.bubbles, eventInit.cancelable);
}
return el.dispatchEvent(event);
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# EventEmitter(发布订阅模式)
class EventEmitter {
constructor() {
this.cache = {}
}
on(name, fn) {
if (this.cache[name]) {
this.cache[name].push(fn)
} else {
this.cache[name] = [fn]
}
}
off(name, fn) {
let tasks = this.cache[name]
if (tasks) {
const index = tasks.findIndex(f => f === fn || f.callback === fn)
if (index >= 0) {
tasks.splice(index, 1)
}
}
}
emit(name, once = false, ...args) {
if (this.cache[name]) {
// 创建副本,如果回调函数内继续注册相同事件,会造成死循环
let tasks = this.cache[name].slice()
for (let fn of tasks) {
fn(...args)
}
if (once) {
delete this.cache[name]
}
}
}
}
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
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
# 自定义事件event
// 新建事件实例
var event = new Event('build');
// 添加监听函数
elem.addEventListener('build', function (e) { ... }, false);
// 触发事件
elem.dispatchEvent(event);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 事件循环(Event Loop)
- 最后一次搞懂 Event Loop (opens new window)
- 原地址:从 promise、process.nextTick、setTimeout 出发,谈谈 Event Loop 中的 Job queue #5 (opens new window)
- 执行顺序
同步代码—>microTask—>macroTask
# 事件的传播(事件流)
- 什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2 级事件流包括下面几个阶段的三个阶段:
- 第一阶段:从 window 对象传导到目标节点,称为"
捕获阶段
"(capture phase) - 第二阶段:在目标节点上触发,称为"
目标阶段
"(target phase) - 第三阶段:从目标节点传导回 window 对象,称为"
冒泡阶段
"(bubbling phase) IE只支持事件冒泡
# 事件委托
- 利用事件冒泡的原理,将事件绑定在父容器中,让父容器代为触发
var ul = document.querySelector("ul");
ul.addEventListener("click", function(event) {
if (event.target.tagName.toLowerCase() === "li") {
// some code
}
});
// 阻止当前监听函数的传播
//stopPropagation方法只会阻止当前监听函数的传播,
//不会阻止<p>节点上的其他click事件的监听函数
p.addEventListener("click", function(event) {
event.stopPropagation();
});
//如果想要不再触发那些监听函数,可以使用stopImmediatePropagation方法
p.addEventListener("click", function(event) {
event.stopImmediatePropagation();
});
p.addEventListener("click", function(event) {
// 不会被触发
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# touch长按事件
var touch = {
/* 长按事件 */
longTap: function (dom, callback) {
if (dom && typeof dom == 'object') {
var startX, startY, startTime, isLongTime = false;
dom.addEventListener('touchstart', function (e) {
startTime = Date.now();
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}, false);
dom.addEventListener('touchend', function (e) {
e.stopPropagation();
e.preventDefault();
var endX = e.changedTouches[0].clientX;
var endY = e.changedTouches[0].clientY;
if (Math.abs(endX - startX) < 6 && Math.abs(endY - startY) < 6) {
isLongTime = true;
} else {
isLongTime = false;
}
if ((+new Date()) - startTime > 200 && isLongTime) {
callback && callback(e);
}
}, false);
}
}
}
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
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
# touch双击事件
var touch = {
/* 双击事件 */
doubleTap: function (dom, callback) {
if (!dom) {
return false;
}
var isTouchEnd = false,
lastTime = 0,
lastTx = null,
lastTy = null,
firstTouchEnd = true,
body = document.body,
dTapTimer, startTx, startTy, startTime;
dom.addEventListener('touchstart', function (e) {
if (dTapTimer) {
clearTimeout(dTapTimer);
dTapTimer = null;
}
var touches = e.touches[0];
startTx = touches.clientX;
startTy = touches.clientY;
}, false);
dom.addEventListener('touchend', function (e) {
var touches = e.changedTouches[0],
endTx = touches.clientX,
endTy = touches.clientY,
now = Date.now(),
duration = now - lastTime;
// 首先要确保能触发单次的 tap 事件
if (Math.abs(startTx - endTx) < 6 && Math.abs(startTx - endTx) < 6) {
// 两次 tap 的间隔确保在 500 毫秒以内
if (duration < 301) {
// 本次的 tap 位置和上一次的 tap 的位置允许一定范围内的误差
if (lastTx !== null &&
Math.abs(lastTx - endTx) < 45 &&
Math.abs(lastTy - endTy) < 45) {
firstTouchEnd = true;
lastTx = lastTy = null;
callback && callback(e);
}
} else {
lastTx = endTx;
lastTy = endTy;
}
} else {
firstTouchEnd = true;
lastTx = lastTy = null;
}
lastTime = now;
}, false);
// 在 iOS 的 safari 上手指敲击屏幕的速度过快,
// 有一定的几率会导致第二次不会响应 touchstart 和 touchend 事件
// 同时手指长时间的touch不会触发click
if (~navigator.userAgent.toLowerCase().indexOf('iphone os')) {
body.addEventListener('touchstart', function (e) {
startTime = Date.now();
}, true);
body.addEventListener('touchend', function (e) {
var noLongTap = Date.now() - startTime < 501;
if (firstTouchEnd) {
firstTouchEnd = false;
if (noLongTap && e.target === dom) {
dTapTimer = setTimeout(function () {
firstTouchEnd = true;
lastTx = lastTy = null;
callback && callback(e);
}, 400);
}
} else {
firstTouchEnd = true;
}
}, true);
// iOS 上手指多次敲击屏幕时的速度过快不会触发 click 事件
dom.addEventListener('click', function (e) {
if (dTapTimer) {
clearTimeout(dTapTimer);
dTapTimer = null;
firstTouchEnd = true;
}
}, false);
}
}
}
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
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
# touch向上滑动事件
var touch = {
/*向上滑动事件*/
swipeUp: function (dom, callback) {
if (!dom) {
return false;
}
var isTouchMove, startTx, startTy;
dom.addEventListener('touchstart', function (e) {
var touches = e.touches[0];
startTx = touches.clientX;
startTy = touches.clientY;
isTouchMove = false;
}, false);
dom.addEventListener('touchmove', function (e) {
isTouchMove = true;
e.preventDefault();
}, false);
dom.addEventListener('touchend', function (e) {
if (!isTouchMove) {
return;
}
var touches = e.changedTouches[0],
endTx = touches.clientX,
endTy = touches.clientY,
distanceX = startTx - endTx
distanceY = startTy - endTy,
isSwipe = false;
if (Math.abs(distanceX) < Math.abs(distanceY)) {
if (distanceY > 20) {
callback();
isSwipe = true;
}
}
}, false);
}
}
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
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
# touch向下滑动事件
var touch = {
/*向下滑动事件*/
swipeDown: function (dom, callback) {
if (!dom) {
return false;
}
var isTouchMove, startTx, startTy;
dom.addEventListener('touchstart', function (e) {
var touches = e.touches[0];
startTx = touches.clientX;
startTy = touches.clientY;
isTouchMove = false;
}, false);
dom.addEventListener('touchmove', function (e) {
isTouchMove = true;
e.preventDefault();
}, false);
dom.addEventListener('touchend', function (e) {
if (!isTouchMove) {
return;
}
var touches = e.changedTouches[0],
endTx = touches.clientX,
endTy = touches.clientY,
distanceX = startTx - endTx
distanceY = startTy - endTy,
isSwipe = false;
if (Math.abs(distanceX) < Math.abs(distanceY)) {
if (distanceY < -20) {
callback();
isSwipe = true;
}
}
}, false);
}
}
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
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
# touch向左滑动事件
var touch = {
/*向左滑动事件*/
swipeLeft: function (dom, callback) {
if (!dom) {
return false;
}
var isTouchMove, startTx, startTy;
dom.addEventListener('touchstart', function (e) {
var touches = e.touches[0];
startTx = touches.clientX;
startTy = touches.clientY;
isTouchMove = false;
}, false);
dom.addEventListener('touchmove', function (e) {
isTouchMove = true;
e.preventDefault();
}, false);
dom.addEventListener('touchend', function (e) {
if (!isTouchMove) {
return;
}
var touches = e.changedTouches[0],
endTx = touches.clientX,
endTy = touches.clientY,
distanceX = startTx - endTx
distanceY = startTy - endTy,
isSwipe = false;
if (Math.abs(distanceX) >= Math.abs(distanceY)) {
if (distanceX > 20) {
callback();
isSwipe = true;
}
}
}, false);
}
}
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
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
# touch向右滑动事件
var touch = {
/*向右滑动事件*/
swipeRight: function (dom, callback) {
if (!dom) {
return false;
}
var isTouchMove, startTx, startTy;
dom.addEventListener('touchstart', function (e) {
var touches = e.touches[0];
startTx = touches.clientX;
startTy = touches.clientY;
isTouchMove = false;
}, false);
dom.addEventListener('touchmove', function (e) {
isTouchMove = true;
e.preventDefault();
}, false);
dom.addEventListener('touchend', function (e) {
if (!isTouchMove) {
return;
}
var touches = e.changedTouches[0],
endTx = touches.clientX,
endTy = touches.clientY,
distanceX = startTx - endTx
distanceY = startTy - endTy,
isSwipe = false;
if (Math.abs(distanceX) >= Math.abs(distanceY)) {
if (distanceX < -20) {
callback();
isSwipe = true;
}
}
}, false);
}
}
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
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