事件流
形容的是从页面中承受事件的程序。事件最早是在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
就像往水里扔了一块石头,水波会从里往外扩散,同样,因为三个元素嵌套,外面的元素是属于里面元素的,点击外面元素其实也是在点击里面的元素,所以事件的回调会从外向外流传执行。
所有古代浏览器都反对事件冒泡,不过还是有所区别:
- IE9、Firefox、chrome、Safari: 指标元素-->外层元素....-->body-->html-->document-->window
- IE9以下:指标元素-->外层元素....-->body-->html-->document
- IE5.5及更早IE:指标元素-->外层元素....-->body-->document (有待验证)
事件捕捉
事件捕捉的思维是不太具体的的节点应该更早承受到事件,而最具体的节点应该最初承受到事件。事件捕捉的意义在于事件达到预约指标之前捕捉它。---《JavaScript高级程序设计》
还是以下面的代码构造为例,点击box3
,如果事件是以捕捉的形式触发,那么会输入如下:
box1 box2 box3
就像拆快递一样,从外往里,一层一层,直到看到你买的宝贝,同样,事件的回调也会从外往里执行,直到指标元素,也就是点击的那个元素box3
。同样这个最外层元素对于不同浏览器也是有区别的,能够参考下面的事件冒泡
因为老版本的浏览器(IE8及更早版本)不反对捕捉,因为很少有人应用事件捕捉.举荐大家放心使用冒泡,因为古代浏览器都反对,在有非凡须要时再应用事件捕捉。
DOM事件流
因为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事件处理程序
某个元素反对的每种事件,都能够应用一个与相应事件处理程序同名的HTML个性来指定。这个个性的值应该是可能执行的Javascript代码。
说人话,就是应用一个与相应事件处理程序同名的HTML属性来指定事件处理程序:
- 写法1
<button onclick="console.log(this,event)">点击</button>
点击输入如下:
<button onclick="console.log(this)">点击</button> MouseEvent {isTrusted: true, constructor: Object}
---
- 写法2
<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} }
特点:
- 写法1间接把代码全副写在
html
标签内时,this
值为事件指标元素 - 写法2把函数抽离进去,
this
值为window
, - 将事件函抽离进去,可能会呈现在函数解析之前点击的状况,会报错,能够应用
try/catch
包起来 - 写法1和2都能够应用关键字
event
间接拜访事件对象,不须要定义或者从参数中获取
DOM0级事件处理程序
通过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级事件处理程序
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的输入 <div id="box4">点击我</div> 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)
特点:
- 同一事件能够指定多个处理程序函数,会按绑定程序顺次执行
- this指向指标元素本身
- 能够间接通过
event
关键字获取事件对象 - 事件流在达到指标元素时,局部捕捉和冒泡,按绑定程序执行
- 若要移除事件,则事件处理程序对应的函数必须是具名函数,且
removeEventListener
和addEventListener
的三个参数必须统一
IE事件处理程序
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
元素,输入如下:
- IE10、IE9
box4我是box4的输入[object Window] [object MSEventObj]box3box2box2正本box1
- IE8及以下
[object Window] [object Object]我是box4的输入box4box3box2正本box2box1
移除事件处理程序
<div id="box">点击我</div>
js代码
var btn = document.getElementById('box');btn.attachEvent('click',clickHandler)function clickHandler(){ console.log('box')}//移除事件处理程序btn.detachEvent('click',clickHandler)
特点:
- 第一个参数是事件处理程序,不是事件名,须要加
on
- 事件处理程序在全局作用域运行,this指向
window
- 事件处理程序函数外部能够间接应用
event
关键字获取事件对象 - 同一元素同一事件,能够指定多个事件处理程序,
IE9、IE10
按绑定程序执行,IE8
及以下按倒序执行 - 若要移除事件处理程序,事件处理程序对应函数应该应用具名函数,
detachEvent()
办法须要和attachEvent()
办法两个参数统一
DOM0级事件处理程序和DOM2级事件处理程序优先级
<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级事件和DOM2级事件能够共存
- 因为DOM0级只有冒泡,所以先执行DOM2级的捕捉阶段,而后在冒泡阶段,DOM0级和DOM2级无优先级,按绑定程序执行
留神:DOM0级、DOM1级、DOM2级这些都是W3C举荐的一种规范,之所以没说DOM1级事件,是因为DOM1级次要定义的是HTML和XML文档的底层构造,没有对于事件的局部,而DOM2级在DOM1级的根底上引入了更多交互能力,就包含DOM2级事件
创作不易,如果你感觉文章对你有帮忙,能够关注我的集体公众号 前端V
,感激你的反对!!