JS事件流是在前端面试过程中常常被提到的点,伴随而来的是冒泡流、捕获流及中间过程中的问题,我们逐一分析。
事件Event
事件是可以被Javascript侦测到的行为,是我们有能力通过js创建动态页面。比如用户点击某按钮、鼠标经过某片区域等等,都属于事件。
网页事件定义有很多,我们通过在控制台中打印具体的DOM元素可以看到具体支持的事件,甚至可以自己书写自定义事件。所有在打印元素中以 on 开头后面的基本上都是事件了。
比较常用的事件举例:
- 鼠标点击
- 页面或图像载入
- 鼠标悬浮于页面的某个热点之上
- 在表单中选取输入框
- 确认表单
- 键盘按键
通常事件都需要与函数配合,或者添加监听事件。当事件发生时,方能触发函数执行。
事件流
事件流解决的时候,满足同时触发多个事件时,执行顺序的问题。JS事件流的原理如图:
一个完整的事件流是从window开始,最后回到window的过程,事件并且被分为捕获阶段、目标过程、冒泡过程。
当年IE和网景公司的浏览器大战中,IE提出的是冒泡流从下到上,网景提出的是捕获流从上到下。W3C组织统一了下,大家都支持。但是IE6、IE7、IE8只支持冒泡流,你能有啥办法。为了兼容性,推荐大家使用冒泡流。
还有关于整个流程有个DOM Level的玩意,DOM Level 0不支持捕获事件。
在事件绑定时候,我们使用 addEventListener 进行事件绑定,addEventListener默认使用冒泡流,第三个参数设为true的时候,则使用捕获流。
举例
<div id='one'>
<div id='two'>
<div id='three'>
<div id='four'>
Event
</div>
</div>
</div>
</div>
<script type='text/javascript'>
var one=document.getElementById('one');
var two=document.getElementById('two');
var three=document.getElementById('three');
var four=document.getElementById('four');
one.addEventListener('click',function(){
alert('one');
},false);
two.addEventListener('click',function(){
alert('two');
},false);
three.addEventListener('click',function(){
alert('three');
},false);
four.addEventListener('click',function(){
alert('four');
},false);
</script>
点击之后,弹框顺序就是 four、three、two、one
然后把addEventListener的第三个参数都变成true之后
<script type='text/javascript'>
var one=document.getElementById('one');
var two=document.getElementById('two');
var three=document.getElementById('three');
var four=document.getElementById('four');
one.addEventListener('click',function(){
alert('one');
},true);
two.addEventListener('click',function(){
alert('two');
},true);
three.addEventListener('click',function(){
alert('three');
},true);
four.addEventListener('click',function(){
alert('four');
},true);
</script>
弹框的顺序就改变成 one、two、three、four。 至于混合呢?那你自己试试吧。
事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的javascript代码。
<input type="button" value="click me" onclick="alert('clicked')"/>
这样,当点击按钮,就可以弹警告框了。这个操作是通过指定onclick特性并将一些javascript代码作为它的值来定义的。由于这个值是javascript,因此不能在其中使用未经转义的HTML语法字符。事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。我们以window为例,支持的事件处理程序差不多有100多种,文末有列出来window打印出来所有的事件列表。
事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。
currentTarget、target
target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(一般为父级)。
换句话说,event.target指向引起触发事件的元素,而event.currentTarget则是事件绑定的元素
在事件处理程序内部,对象this始终等于currentTarget的值,而target只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget、target包含相同的值。
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this); //true
}
由于click事件的目标是按钮,因此这三个值是相等的。 如果事件处理程序存在于按钮的父节点中,那么这些值是不相同的:
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
}
当单击这个例子中的按钮时,this和currentTarget都等于document.body,因为事件处理程序是注册到这个元素上的。然而target元素却等于按钮元素,因为它是click的真正目标。由于按钮上没有注册事件处理程序,结果click事件就冒泡到了document.body上了,在那里事件才得到了处理。
type
在需要通过一个函数处理多个事件时,可以使用type属性。
var handler = function(event){
switch(event.type){
case "click":
alert("clicked");
break;
case "mouseover":
event.target.style,backgroundColor = "red";
break;
case "mouseout":
event.target.style.backgroundColor = "";
break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
这个例子定义了一个名为handler的函数,用于处理3种事件:click、mouseover、mouseout。函数中通过检测event.type属性,让函数能够确定发生了什么事件,并执行相应的操作。
preventDefault()
要阻止特定事件的默认行为,可以使用这个方法。例如,链接的默认行为是被单击时会导航到其href特性指定的URL。如果想阻止这一默认行为,那么通过连接的onclick事件处理程序可以取消它。
link.onclick = function(event){
event.preventDefault();
}
只有cancelable属性设为true的事件,才可以使用preventDefault()来取消其默认行为。
stopPropagation()
用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。 例如,直接添加到一个按钮的事件处理程序可以调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序。
btn.onclick = function(event){
alert("clicked");
event.stopPropagation();
}
document.body.onclick = function(event){
alert("body clicked");
}
对于这个例子而言,如果不调用stopPropagation(),就会在单击按钮时出现两个警告框。使用了之后,由于click事件根本不会被传播到document.body,因此就不会触发注册在document.body上的onclick事件程序。
eventPhase
这个属性可以用来确定事件当前正位于事件流的哪个阶段。
捕获阶段:eventPhase = 1; 处于目标对象上:eventPhase = 2; 冒泡阶段:eventPhase = 3; 要注意的是,尽管“处于目标”发生在冒泡阶段,但是eventPhase仍等于2.
btn.onclick = function(event){
alert(event.eventPhase); //2
}
document.body.addEventListener("click",function(event){
alert(event.eventPhase); //1
},true);
document.body.onclick = function(event){
alert(event.eventPhase); //3
}
当单击这个例子中的按钮时:
首先执行的事件处理程序是在捕获阶段触发的添加到document.body中的那一个,会弹出一个警告框显示1 其次会触发在按钮上注册的事件处理程序,此时eventPhase为2 最后在冒泡阶段触发添加到document.body中的那一个,显示eventPhase为3。 当eventPhase等于2时,this、target、currentTarget始终都是相等的。
只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。
Window 事件属性
window 对象触发的事件。
适用于 <body> 标签:
属性 | 值 | 描述 |
---|---|---|
onafterprint | script | 在打印文档之后运行脚本 |
onbeforeprint | script | 在文档打印之前运行脚本 |
onbeforeonload | script | 在文档加载之前运行脚本 |
onblur | script | 当窗口失去焦点时运行脚本 |
onerror | script | 当错误发生时运行脚本 |
onfocus | script | 当窗口获得焦点时运行脚本 |
onhaschange | script | 当文档改变时运行脚本 |
onload | script | 当文档加载时运行脚本 |
onmessage | script | 当触发消息时运行脚本 |
onoffline | script | 当文档离线时运行脚本 |
ononline | script | 当文档上线时运行脚本 |
onpagehide | script | 当窗口隐藏时运行脚本 |
onpageshow | script | 当窗口可见时运行脚本 |
onpopstate | script | 当窗口历史记录改变时运行脚本 |
onredo | script | 当文档执行再执行操作(redo)时运行脚本 |
onresize | script | 当调整窗口大小时运行脚本 |
onstorage | script | 当文档加载加载时运行脚本 |
onundo | script | 当 Web Storage 区域更新时(存储空间中的数据发生变化时) |
onunload | script | 当用户离开文档时运行脚本 |
表单事件
由 HTML 表单内部的动作触发的事件。
适用于所有 HTML 5 元素,不过最常用于表单元素中:
属性 | 值 | 描述 |
---|---|---|
onblur | script | 当元素失去焦点时运行脚本 |
onchange | script | 当元素改变时运行脚本 |
oncontextmenu | script | 当触发上下文菜单时运行脚本 |
onfocus | script | 当元素获得焦点时运行脚本 |
onformchange | script | 当表单改变时运行脚本 |
onforminput | script | 当表单获得用户输入时运行脚本 |
oninput | script | 当元素获得用户输入时运行脚本 |
oninvalid | script | 当元素无效时运行脚本 |
onreset | script | 当表单重置时运行脚本。HTML 5 不支持。 |
onselect | script | 当选取元素时运行脚本 |
onsubmit | script | 当提交表单时运行脚本 |
键盘事件
由键盘触发的事件。
适用于所有 HTML 5 元素:
属性 | 值 | 描述 |
---|---|---|
onkeydown | script | 当按下按键时运行脚本 |
onkeypress | script | 当按下并松开按键时运行脚本 |
onkeyup | script | 当松开按键时运行脚本 |
鼠标事件
由鼠标或相似的用户动作触发的事件。
适用于所有 HTML 5 元素:
属性 | 值 | 描述 |
---|---|---|
onclick | script | 当单击鼠标时运行脚本 |
ondblclick | script | 当双击鼠标时运行脚本 |
ondrag | script | 当拖动元素时运行脚本 |
ondragend | script | 当拖动操作结束时运行脚本 |
ondragenter | script | 当元素被拖动至有效的拖放目标时运行脚本 |
ondragleave | script | 当元素离开有效拖放目标时运行脚本 |
ondragover | script | 当元素被拖动至有效拖放目标上方时运行脚本 |
ondragstart | script | 当拖动操作开始时运行脚本 |
ondrop | script | 当被拖动元素正在被拖放时运行脚本 |
onmousedown | script | 当按下鼠标按钮时运行脚本 |
onmousemove | script | 当鼠标指针移动时运行脚本 |
onmouseout | script | 当鼠标指针移出元素时运行脚本 |
onmouseover | script | 当鼠标指针移至元素之上时运行脚本 |
onmouseup | script | 当松开鼠标按钮时运行脚本 |
onmousewheel | script | 当转动鼠标滚轮时运行脚本 |
onscroll | script | 当滚动元素滚动元素的滚动条时运行脚本 |
媒介事件
由视频、图像以及音频等媒介触发的事件。
适用于所有 HTML 5 元素,不过在媒介元素(诸如 audio、embed、img、object 以及 video)中最常用:
属性 | 值 | 描述 |
---|---|---|
onabort | script | 当发生中止事件时运行脚本 |
oncanplay | script | 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本 |
oncanplaythrough | script | 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本 |
ondurationchange | script | 当媒介长度改变时运行脚本 |
onemptied | script | 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本 |
onended | script | 当媒介已抵达结尾时运行脚本 |
onerror | script | 当在元素加载期间发生错误时运行脚本 |
onloadeddata | script | 当加载媒介数据时运行脚本 |
onloadedmetadata | script | 当媒介元素的持续时间以及其他媒介数据已加载时运行脚本 |
onloadstart | script | 当浏览器开始加载媒介数据时运行脚本 |
onpause | script | 当媒介数据暂停时运行脚本 |
onplay | script | 当媒介数据将要开始播放时运行脚本 |
onplaying | script | 当媒介数据已开始播放时运行脚本 |
onprogress | script | 当浏览器正在取媒介数据时运行脚本 |
onratechange | script | 当媒介数据的播放速率改变时运行脚本 |
onreadystatechange | script | 当就绪状态(ready-state)改变时运行脚本 |
onseeked | script | 当媒介元素的定位属性 [1] 不再为真且定位已结束时运行脚本 |
onseeking | script | 当媒介元素的定位属性为真且定位已开始时运行脚本 |
onstalled | script | 当取回媒介数据过程中(延迟)存在错误时运行脚本 |
onsuspend | script | 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本 |
ontimeupdate | script | 当媒介改变其播放位置时运行脚本 |
onvolumechange | script | 当媒介改变音量亦或当音量被设置为静音时运行脚本 |
onwaiting | script | 当媒介已停止播放但打算继续播放时运行脚本 |
文末
JS事件及事件流是js开发中重要切常用的一环,所有的交互都离不开事件。前端开发的小伙伴如果没吃透或者事件不清楚的,可以在过一过。