描述的是从页面中接受事件的顺序。事件最早是在
IE3
和Netscape Navigator2
中出现的,在IE4
和Navigator4
发布时,这两种浏览器都提供了相似但不相同的API
。IE
的事件流是事件冒泡流,而Netscape
的事件流是事件捕获流
即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点—《JavaScript高级程序设计》
<div id="box1">
<div id="box2">
<div id="box3">点击我div>
div>
div>
如上面代码结构所示,三个嵌套元素,假如我给三个元素都绑定点击事件,并且在事件回调中输出它们各自的ID,然后点击最里层的元素目标(目标元素box3),输出如下:
box3
box2
box1
就像往水里扔了一块石头,水波会从里往外扩散,同样,因为三个元素嵌套,里面的元素是属于外面元素的,点击里面元素其实也是在点击外面的元素,所以事件的回调会从内向外传播执行。
所有现代浏览器都支持事件冒泡,不过还是有所区别:
事件捕获的思想是不太具体的的节点应该更早接受到事件,而最具体的节点应该最后接受到事件。事件捕获的意义在于事件到达预定目标之前捕获它。—《JavaScript高级程序设计》
还是以上面的代码结构为例,点击box3
,如果事件是以捕获的方式触发,那么会输出如下:
box1
box2
box3
就像拆快递一样,从外往里,一层一层,直到看到你买的宝贝,同样,事件的回调也会从外往里执行,直到目标元素,也就是点击的那个元素box3
。同样这个最外层元素对于不同浏览器也是有区别的,可以参考上面的事件冒泡
由于老版本的浏览器(IE8及更早版本)不支持捕获,因为很少有人使用事件捕获.推荐大家放心使用冒泡,因为现代浏览器都支持,在有特殊需要时再使用事件捕获。
由于IE
和Netscpace
两方提出的事件流概念相反,W3C
为了统一标准,折中了一下,在DOM2
级中提出了DOM2
级事件
DOM2级事件规定,事件的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。三个阶段依次发生。
在DOM事件流中,实际的目标在捕获阶段不会接受到事件,捕获阶段会在到达目标元素前结束,然后是“处于目标”阶段,绑定在目标上的事件会根据绑定顺序执行,并在事件处理中被看成冒泡阶段的一部分。最后阶段是冒泡阶段。
IE9、Opera、Firefox、Chrome和Safari都支持DOM事件流;IE8及更早版本不支持DOM事件流
IE8
及更早版本的IE
浏览器不支持DOM2
标准中指定事件处理程序的写法,所以不支持DOM
事件流,注意是不支持DOM
事件流,这里的DOM事件流
指的是DOM2
级中提出的DOM事件流
,就是说不像上面说的那样,具有捕获、目标、冒泡,而是只有冒泡。
事件就是用户或浏览器自身执行的某种动作。比如
click
、load
、mouseover
等等,这都是事件的名字,而响应某个事件的函数就叫事件处理程序(或事件侦听器)
。事件处理程序的名字以on
开头,因此click
事件的事件处理程序就是onclick
。而为事件指定处理程序的方式有好几种。
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的Javascript代码。
说人话,就是使用一个与相应事件处理程序同名的HTML属性来指定事件处理程序:
<button onclick="console.log(this,event)">点击button>
点击输出如下:
MouseEvent {isTrusted: true, constructor: Object}
<button onclick="clickBtn()">点击button>
js代码:
function clickBtn() {
console.log(this); //window
console.log(event); //MouseEvent {isTrusted: true, constructor: Object}
}
这样把函数抽离出来单独定义时,this
值为window
,事件对象event
可以不用定义或者从参数中获取,直接在函数中使用event
关键字获取,注意这种写法,是必须用event
关键字,不能用其他的!
<button onclick="clickBtn(event,123)">点击button>
js代码:
function clickBtn(a, b) {
console.log(this); //window
console.log(event) //MouseEvent {isTrusted: true, constructor: Object}
console.log(a); //MouseEvent {isTrusted: true, constructor: Object}
console.log(b); //123
}
可以传递参数,使用对应位置的参数来接受即可,如果一定要手动把事件对象event
传进来,也可以,但是同样,在传递时也必须使用event
关键字,在而在函数中,也必须使用对应位置的形参来接收,比如上面的a
,但是同时,你还是可以使用evemt
关键字获取事件对象,所以我觉得没有必要手动传递事件对象
使用try{}catch(err){}
优化,防止报错
<button onclick="try{clickBtn()}catch(err){console.log(err)}">点击button>
js代码
function clickBtn() {
console.log(this); //window
console.log(event); //MouseEvent {isTrusted: true, constructor: Object}
}
特点:
html
标签内时,this
值为事件目标元素this
值为window
,try/catch
包起来event
直接访问事件对象,不需要定义或者从参数中获取通过
JavaScript
指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
<button id="btn">点击button>
js代码:
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log(this.id);
console.log(event);
console.log("被点击了");
};
btn.onclick = function() {
console.log(this.id); //btn
console.log(event); //MouseEvent {isTrusted: true, constructor: Object}
console.log("哈哈哈哈"); //哈哈哈哈
};
// 删除事件处理程序
btn.onclick = null
this
指向当前元素btn.onclick
置为null
DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:
addEventListener()
和removeEventListener()
。所有DOM节点都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值如果是true
,表示在捕获阶段调用事件处理程序;如果是false
,表示在冒泡阶段调用事件处理程序。
<div id="box1">
<div id="box2">
<div id="box3">
<div id="box4">点击我div>
div>
div>
div>
js代码:
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var box4 = document.getElementById("box4");
box1.addEventListener("click",function() {console.log("box1")})
box2.addEventListener("click",function() {console.log("box2")});
box3.addEventListener("click",function() {console.log("box3")});
//为box4的click事件绑定两个函数
box4.addEventListener("click",function() {console.log("box4")});
box4.addEventListener("click",function() {console.log("我是box4的输出")});
//输出this和event
box4.addEventListener("click",function() {console.log(this,event)});
点击id
为box4
的元素输出:
box4
我是box4的输出
点击我 MouseEvent {isTrusted: true, constructor: Object}
box3
box2
box1
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var box4 = document.getElementById("box4");
box1.addEventListener("click",function() {console.log("box1")},true);
box2.addEventListener("click",function() {console.log("box2")},true);
box3.addEventListener("click",function() {console.log("box3")},true);
box4.addEventListener("click",function() {console.log("box4")},true);
box4.addEventListener("click",function() {console.log("我是box4的输出");});
点击id
为box4
的元素输出:
box1
box2
box3
box4
我是box4的输出
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var box4 = document.getElementById("box4");
box1.addEventListener("click",function() {console.log("box1")});
box2.addEventListener("click",function() {console.log("box2")},true);
box3.addEventListener("click",function() {console.log("box3")});
box4.addEventListener("click",function() {console.log("我是box4的输出")});
box4.addEventListener("click",function() {console.log("box4")},true);
点击id
为box4
的元素输出:
box2
我是box4的输出
box4
box3
box1
上面这个例子就有点意思了,事件流不是按捕获、目标、冒泡的顺序吗?那不应该是依次输出box2、box4、我是box4的输出、box3、box1
吗?为啥这里是先输出我是box4的输出
再输出box4
?
如果你有疑问,可以往前翻,事件流里面讲到在DOM事件流中,实际的目标在捕获阶段不会接受到事件
,怎么理解?可以认为,在给目标元素添加事件时,第三个参数指定为true
,即在捕获阶段调用,是无效的,所以就相当于第三个参数是false
,也就是默认值,这也就是为什么说处于目标
阶段的事件会被看成冒泡阶段的一部分
,既然是冒泡,那就按绑定顺序执行,所以就先输出我是box4的输出
<div id="box">点击我div>
js代码:
var btn = document.getElementById('box');
btn.addEventListener('click',clickHandler,true)
function clickHandler(){
console.log('box')
}
//移除事件处理程序
btn.removeEventListener('click',clickHandler,true)
event
关键字获取事件对象removeEventListener
和addEventListener
的三个参数必须一致
IE
中实现了与DOM2
级类似的两个方法:attachEvent()
和detachEvent()
。这两个方法接受两个参数:事件处理程序名称和事件处理程序函数。由于IE8
及更早版本只支持事件冒泡,所以通过这种方法添加的事件处理程序都会被添加到冒泡阶段
只有IE10及以下版本的IE和Opera浏览器支持IE事件处理程序
<div id="box1">
<div id="box2">
<div id="box3">
<div id="box4">点击我div>
div>
div>
div>
js代码
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var box4 = document.getElementById("box4");
box1.attachEvent("onclick", function() {
console.log("box1");
});
box2.attachEvent("onclick", function() {
console.log("box2");
});
box2.attachEvent("onclick", function() {
console.log("box2副本");
});
box3.attachEvent("onclick", function() {
console.log("box3");
});
box4.attachEvent("onclick", function() {
console.log("box4");
});
box4.attachEvent("onclick", function() {
console.log("我是box4的输出");
});
box4.attachEvent("onclick", function() {
console.log(this, event);
});
点击id
为box4
元素,输出如下:
box4
我是box4的输出
[object Window] [object MSEventObj]
box3
box2
box2副本
box1
[object Window] [object Object]
我是box4的输出
box4
box3
box2副本
box2
box1
<div id="box">点击我div>
js代码
var btn = document.getElementById('box');
btn.attachEvent('click',clickHandler)
function clickHandler(){
console.log('box')
}
//移除事件处理程序
btn.detachEvent('click',clickHandler)
on
window
event
关键字获取事件对象IE9、IE10
按绑定顺序执行,IE8
及以下按倒序执行detachEvent()
方法需要和attachEvent()
方法两个参数一致 <div id="box1">
<div id="box2">
<div id="box3">
<div id="box4">点击我div>
div>
div>
div>
js代码
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var box4 = document.getElementById("box4");
box1.onclick = function(){
console.log('box1的DOM0级冒泡')
}
box1.addEventListener("click", function() {
console.log("box1的DOM2级捕获");
},true);
box2.addEventListener("click", function() {
console.log("box2的DOM2级捕获");
},true);
box2.onclick = function(){
console.log('box2的DOM0级冒泡')
}
box3.onclick = function(){
console.log('box3的DOM0级冒泡')
}
box3.addEventListener("click", function() {
console.log("box3的DOM2级冒泡");
});
box4.addEventListener("click", function() {
console.log("box4的DOM2级");
});
box4.onclick = function(){
console.log('box4的DOM0级')
}
点击id
为box4
元素,输出如下:
box1的DOM2级捕获
box2的DOM2级捕获
box4的DOM2级
box4的DOM0级
box3的DOM0级冒泡
box3的DOM2级冒泡
box2的DOM0级冒泡
box1的DOM0级冒泡
注意:DOM0级、DOM1级、DOM2级这些都是W3C推荐的一种标准,之所以没说DOM1级事件,是因为DOM1级主要定义的是HTML和XML文档的底层结构,没有关于事件的部分,而DOM2级在DOM1级的基础上引入了更多交互能力,就包括DOM2级事件
整理不易,如果你觉得文章对你有帮助,可以关注我的个人公众号 前端V
,感谢你的支持!!