# 事件
事件流描述的是从页面中接收事件的顺序。事件冒泡即事件开始时由最具体的元素接收,然后逐渐向上传播到较为不具体的节点。事件捕获是不太具体的节点更早接收到事件,而具体节点最后接收到事件。当事件触发时,首先经历的是一个捕获过程;事件会从最外层的元素开始穿梭,逐层穿梭到最内层元素。这个穿梭过程会持续到事件抵达它目标的元素位置。此时事件流就切换到了目标阶段--事件被目标元素所接收。然后事件会回弹,进入冒泡阶段
DOM2级
规定事件流包括三个阶段:事件捕获,处于目标,事件冒泡阶段。在DOM
事件流中,实际的目标在捕获阶段不会收到事件。
# 事件处理程序
TIP
- 利用
on
绑定多个事件后面的会覆盖前面的,利用addEventListener
就不会了 on
是直接将函数赋值给事件,addEventListener
则是利用多个参数
window.onload = function(){
var box = document.getElementById("box");
box.onclick = function(){
console.log("我是box1");
}
box.onclick = function(){
box.style.fontSize = "18px";
console.log("我是box2");
}
}
box.addEventListener("click",function(){
console.log("box");
})
2
3
4
5
6
7
8
9
10
11
12
13
14
第三个参数默认为
false
表示事件按照事件冒泡的执行顺序进行。true
为捕获顺序进行。也可以传入一个对象option
option: {
capture : Boolean // 和上面单个参数一样
once: Boolean // 表示listener在添加之后最多只调用一次。如果是 true,listener会在其被调用之后自动移除。
passive: Boolean //表示listener永远不会调用preventDefault()。如果listener仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
}
2
3
4
5
# HTML
事件处理程序
<input type="button" onclick="alert('event.type')>
# DOM0
级事件处理程序
这种模型不会传播,所以没有事件流的概念,但是现有的浏览器支持以冒泡的方式实现,可以在网页中直接定义监听函数,也可以通过
js
属性来监听函数。这种方式是所有浏览器都兼容的。因此,事件处理程序会在元素的作用域中运行,即this
等于元素。
var btn = document.getElementById('mybtn');
btn.onclick=function() {
var event = window.event;
alert(event.type) // click
}
// 移除事件处理程序
btn.onclick = null
2
3
4
5
6
7
8
# DOM2
级事件处理程序
用于处理指定和删除事件处理程序的操作:addEventListener()
和removeEventListener()
。接收三个参数,第一个参数为事件名,第二个参数事件执行函数,第三个为布尔值(true
代表捕获阶段调用事件处理程序,false
表示冒泡阶段,默认为后者,两个阶段只能有一个执行。)
var btn = document.getElementById('mybtn');
btn.addEventListener('click', function() {
console.log('这里表示冒泡阶段调用事件处理程序')
}, false)
2
3
4
与
DOM0
方式类似,这个事件处理程序同样在被附加到的元素的作用域中运行。使用DOM2
方式的主要优势是可以为同一个事件添加多个事件处理程序。多个事件处理程序以添加顺序来触发
# IE
事件处理程序
在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。这种模型可以添加多个监听函数,会按顺序依次执行。 IE中类似的事件处理程序操作为
attachEvent
和detachEvent
。IE8
以下只支持冒泡。第一个参数是完整事件,例如onclick
,而不是事件名click
。
WARNING
attachEvent()
添加的事件处理程序会添加到冒泡阶段。- 在
IE
中使用attachEvent()
与使用DOM0
方式的主要区别是事件处理程序的作用域。使用DOM0
方式时,事件处理程序中的this
值等于目标元素。而使用attachEvent()
时,事件处理程序是在全局作用域中运行的,因此this
等于window
。 - 与使用
addEventListener()
一样,使用attachEvent()
方法也可以给一个元素添加多个事件处理程序。不过,与DOM
方法不同,这里的事件处理程序会以添加它们的顺序反向触发。 - 与使用
DOM
方法类似,作为事件处理程序添加的匿名函数也无法移除。但只要传给detachEvent()
方法相同的函数引用,就可以移除。
# 事件对象
# DOM中的事件对象
通过
event
变量,可以直接访问事件对象。在事件函数内部,this
等于事件的目标对象(currentTarget
的值),而target
则只包含事件的实际目标。如果直接将事件处理程序指定给目标元素,则this
/currentTarget
和target
包含相同的值
<ul>
<li>vue</li>
<li>css</li>
<li>js</li>
</ul>
2
3
4
5
let ul = document.getElementsByTagName('ul')[0]
ul.onclick = function(e) {
console.log(this,e.currentTarget,e.target) // ul,ul,li
}
2
3
4
在事件冒泡阶段,
e.currentTarget
和e.target
是不相等的,但是在事件的目标阶段两者相等
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
cancelable | Boolean | 只读 | 表明是否可以取消默认行为 |
currentTarget | Element | 只读 | 事件处理程序正在处理的那个元素添加监听事件的对象 |
defaultPrevented | Boolean | 只读 | 为true 表示已经执行了取消默认行为事件 preventDefault () |
detail | Integer | 只读 | 与事件相关细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段,1 为捕获,2 为处于目标,3 为冒泡 |
preventDefault() | Function | 只读 | 取消默认行为,只有cancelable 为true 才可以 |
target | Element | 只读 | 事件目标指向触发事件监听的对象 |
type | String | 只读 | 被触发事件类型 |
stopPropagation() | Function | 只读 | 取消事件进一步捕获/冒泡,只有bubbles 为true 有效 |
stopImmediatePropagation
同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件。
# IE中的事件对象
- DOM0添加事件处理程序时。
event
作为window
的一个属性
var btn = document.getElementById('mybtn');
btn.onclick=function() {
var event = window.event;
alert(event.type) // click
}
2
3
4
5
- 使用
attachEvent
则将会有一个event
对象作为参数传入事件处理程序函数中。
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
cancelBubble | Boolean | 读/写 | 默认值为false ,但将其设置为true 就可以取消事件冒泡(与DOM 中的stopPropagation() 方法的作用相同 |
returnValue | Boolean | 读/写 | 默认值为true ,但将其设置为false 就可以取消事件的默认行为(与DOM 中的preventDefault() 方法的作用相同 |
srcElement | Element | 只读 | 事件的目标(与DOM 中的target 属性相同 |
type | String | 只读 | 被触发的事件类型 |
由于IE不支持事件捕获,因而只能取消事件冒泡;但
stopPropagation()
可以同时取消事件捕获和冒泡
# 事件委托(事件代理)
事件委托利用的是事件冒泡,最适合采用事件委托技术的事件包括:
click/mousedown/mouseup/keydown/keyup/keypress
TIP
- 节省内存
- 不需要给子节点注销事件
每当事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的js代码之间就会建立一个连接。这种连接越多页面执行就会越慢。采用事件委托技术减少连接数量。另外不需要事件的时候要移除事件处理程序。一般做法是在页面卸载之前通过onunloaded
事件处理程序移除所有事件处理程序。
document.getElementById('father-id').onclick = function(event) {
event = event || window.event
let target = event.target || event.srcElement
if(target.nodeName.toLowerCase() === 'xxx') {
}
}
2
3
4
5
6
7
# 事件类型
# UI事件
load
:当页面完全加载后在window
上触发,当所有框架加载完毕在框架集上触发,当图像加载完毕时在<img>
元素上面触发。- 浏览器窗口被调整到一个新的大小时会触发
resize
事件。在window
上触发。
# 鼠标事件
mouseenter
:在鼠标光标从元素外部首次移动元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。相对应mouseleave
mouseout
:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。mouseover
:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。- 除了
mouseenter/mouseleave
其余鼠标事件均可冒泡,也可以被取消,不过会影响浏览器默认行为。 mousemove
:当鼠标指针在元素内部移动时重复地触发。mouseup
:在用户释放鼠标按钮时触发。click
按下鼠标/键盘回车触发mousewheel/wheel
鼠标滚轮事件。
TIP
坐标位置
clientX/clientY
表示事件发生时鼠标指针在视口中的水平/垂直坐标pageX/pageY
,表示事件在页面什么位置发生(没有滚动条时和上面两个属性相同)screenX/screenY
:表示鼠标在屏幕的位置offsetX/offsetY(FF浏览器为layerX/layerY)
:鼠标点击位置相对于触发事件对象的水平/垂直距离鼠标按钮 只有在主鼠标按钮被单击/或键盘回车键被按下时才会触发
click
事件,因此检测按钮信息并不是必要的。但对于mousedown/mouseup
事件来说其event
对象存在一个button
属性,表示按下/释放的按钮。0
表示主鼠标按钮,1
表示中间鼠标按钮,2
表示次鼠标按钮。mousewheel
包含一个wheelDelta
属性,当用户向前滚动滚轮时wheelDelta
为120
的倍数,相反为-120
的倍数。Firefox则有一个DOMMouseScroll
类似的事件,而滚轮信息保存在detail
属性中,当鼠标向前则为-3
的倍数,相反为3
的倍数。
# 键盘事件
DOM3
推荐使用keydown/keyup
# HTML5事件
contextmenu
自定义菜单事件DOMContentLoaded
事件
TIP
window
的load
事件会在页面中的一切都加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而浪费周折。而DOMContentLoaded
事件则在形成完成的DOM
树之后就会触发,不理会图像,js文件,css文件或其他资源是否已经下载完毕。要处理该事件,可以为document/window
添加相应的事件处理程序(尽管这个事件会冒泡到window
,但它的目标实际上是document
)
在不支持该事件的浏览器,建议在页面加载期间设置一个时间为0
毫秒的超时调用。
hashchange
事件
以便在
url
参数列表(及'#
'后面的所有字符串)发生变化时通知开发人员。由window
对象来调用这个事件处理程序,此时的event
对象包含两个属性:oldURL
/newURL
# 触摸事件/手势事件
# 模拟事件
# DOM
的事件模拟
可以在document对象上使用createEvent()方法创建事件对象。接收一个表示要创建的事件类型字符串参数。
MouseEvents
模拟鼠标事件,返回的对象有initMouseEvent()
方法。KeyboardEvent
创建键盘事件,有initKeyEvent()
方法。
# IE
事件模拟
用document.createEventObject()
方法模拟事件。
# 实现一个跨浏览器通用事件侦听函数
const EventUtils = {
// 分别使用dom0||dom2||IE方式 来绑定事件
// 添加事件
addEvent: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
// 移除事件
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;
}
},
// 获取事件目标
getTarget: function(event) {
return event.target || event.srcElement;
},
// 获取 event 对象的引用,取到事件的所有信息,确保随时能使用 event
getEvent: function(event) {
return event || window.event;
},
// 阻止事件(主要是事件冒泡,因为 IE 不支持事件捕获)
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
};
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
← 节流防抖 Web的一些常用API →