事件流

形容的是从页面中承受事件的程序。事件最早是在IE3Netscape Navigator2中呈现的,在IE4Navigator4公布时,这两种浏览器都提供了类似但不雷同的APIIE的事件流是事件冒泡流,而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事件流

因为IENetscpace两方提出的事件流概念相同,W3C为了统一标准,折中了一下,在DOM2级中提出了DOM2级事件

DOM2级事件规定,事件的事件流包含三个阶段:事件捕捉阶段、处于指标阶段、事件冒泡阶段。三个阶段顺次产生。

在DOM事件流中,理论的指标在捕捉阶段不会承受到事件,捕捉阶段会在达到指标元素前完结,而后是“处于指标”阶段,绑定在指标上的事件会依据绑定程序执行,并在事件处理中被看成冒泡阶段的一部分。最初阶段是冒泡阶段。

IE9、Opera、Firefox、Chrome和Safari都反对DOM事件流;IE8及更早版本不反对DOM事件流

IE8及更早版本的IE浏览器不反对DOM2规范中指定事件处理程序的写法,所以不反对DOM事件流,留神是不反对DOM事件流,这里的DOM事件流指的是DOM2级中提出的DOM事件流,就是说不像下面说的那样,具备捕捉、指标、冒泡,而是只有冒泡。

事件处理程序

事件就是用户或浏览器本身执行的某种动作。比方clickloadmouseover等等,这都是事件的名字,而响应某个事件的函数就叫事件处理程序(或事件侦听器)。事件处理程序的名字以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)});    

点击idbox4的元素输入:

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的输入");});

点击idbox4的元素输入:

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);    

点击idbox4的元素输入:

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关键字获取事件对象
  • 事件流在达到指标元素时,局部捕捉和冒泡,按绑定程序执行
  • 若要移除事件,则事件处理程序对应的函数必须是具名函数,且removeEventListeneraddEventListener的三个参数必须统一

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);    });

点击idbox4元素,输入如下:

  • 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级')    }

点击idbox4元素,输入如下:

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 ,感激你的反对!!