JS事件流(冒泡、捕获)addEventListener详解

前置知识:DOM结构
JS和HTML之间通过事件来进行交互。当文档或者浏览器窗口发生了特殊的交互,就是用监听器来预定事件,以便事件发生时执行响应的代码(观察者模式)。

事件流


随着浏览器的发展,浏览器开发团队遇到了一个问题:页面的哪一个部分会拥有某个特定的事件?举例来说,比如纸上有一组同心圆。把手指在同心圆上,那么手指指向的不止是一个圆,而是纸上的所有圆。所以后来浏览器开发团队认为,当你单击了某个按钮,除了单击了这个按钮,也单击了它的容器元素和整个页面。
事件流描述的是从页面中接收事件的顺序。
事件流有两种模式:分别是事件冒泡和事件捕获。

  • 以下图结构为例来解释事件冒泡和事件捕获
  • 布局图


    示例图.PNG
  • 当我们点击了line-two-center-child这个块元素时。DOM树当中发生的事件流顺序如下图。
    DOM事件流.PNG

事件捕获(event capture)


不太具体的node应该更早接收到事件,而最具体的node应该最后接收到事件。事件捕获的目的在于在事件到达预定目标之前捕获它。
在上图的布局中,绿色箭头代表的就是事件捕获发生时,DOM树中的node接收到事件的先后顺序。可以看到它是由最外层的document一层一层向内部传递事件。

事件冒泡(event bubble)


即为事件开始时从最具体的元素慢慢接收,然后逐级向上传播到较为不具体的节点。即为由最深的node向最外围的node扩散。
在上图布局中,红色箭头代表的就是事件捕获发生时,DOM树中的node接收到事件的先后顺序。可以看到它是由最内层的center-child一层一层向内部传递事件。

DOM事件流


“DOM标准事件流”:包含三个阶段事件捕获阶段、处于目标阶段、事件冒泡阶段。先后顺序是,事件捕获(可以截取事件)=>实际目标接收到事件=>冒泡阶段(可以对这个事件作出响应)

在捕获阶段,捕获不会到达具体的目标节点,到达目标节点处理事件被看作是冒泡阶段的一部分。

DOM0级事件

直接指定每个元素上面的事件处理属性。DOM0级方法指定的事件处理方法认为是元素的方法。因此,这个时候事件处理程序是在元素的作用域中运行的。this指向引用当前元素。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
   console.log(this.id);//myBtn
}

DOM0级事件会在事件流的冒泡阶段被处理。
删除0级事件btn.onclick = null

DOM2级事件

addEventListener('eventName',callback,ifbubble):三个参数分别是事件名字符串,回调函数,布尔值。第三个参数为true,表示在捕获阶段调用事件处理程序。为false,表示在冒泡阶段调用事件处理。默认是使用冒泡。
removeEventListener():移除addEventListener添加的事件

2级事件和0级事件的区别:2级事件可以添加多个事件处理程序,处理顺序会按照它们的添加顺序触发,而0级事件只有最后一次添加能生效。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
   console.log(1);
}
btn.onclick = function(){
   console.log(2);
}
//btn点击后输出2
var btn = document.getElementById("myBtn");
btn.addEventListener('click', function(){
   console.log(1);
},false)
btn.addEventListener('click', function(){
   console.log(2);
},false)
// btn点击后先输出1再输出2
  • 2级事件的问题:如果addEventListener()添加的回调是匿名函数,会无法通过removeEventListener()移除。
btn.addEventListener('click', function(){
   console.log(2);
},false)
btn.removeEventListener('click', function(){
   console.log(2);
},false);//无法删除

因为传入的匿名函数会认为是一个全新的函数。

事件对象对象(event)

在触发DOM上的某个事件的时候,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

btn.addEventListener('click', function(event){
   console.log(event.type);//"click"
},false);

<input type="button" value="click me" oclick="alert(event.type)">

event对象的主要成员

属性方法 类型 说明
currentTarget Element 其事件处理程序当前正在处理的那个元素,比如,因为子node元素的方法触发了,冒泡到父node的的时候,currentTarget指向父node
target Element 事件的目标节点,也就是target一般是不变的,在哪个node触发就是哪个node
type String 被触发的事件类型
bubbles Boolean 表明事件是否冒泡
cancelable Boolean 表明是否可以取消事件的默认行文
defaultPrevented Boolean 为true表明已经调用了preventDefault()
eventPhase Interger 调用事件处理的阶段,1为捕获,2为处于目标,3为冒泡
preventDefault() Function 取消事件的默认行为
stopPropagation() Function 取消事件的进一步捕获或冒泡

在事件回调方法内部,this始终等于currentTarget。只有在事件处理程序执行期间,event对象才会存在;一旦事件狐狸程序执行完成,event对象就会被销毁。

事件委托


在js当中,添加到页面上的eventhandler的数量会直接关系到页面的整体运行性能。因为,每个函数都是对象,会占用内存,内存中对象越多性能就越差,其次,必须事先指定所有eventhandler而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
对于eventhandler过多的问题解决方案就是事件委托

事件委托:利用了事件冒泡,只指定一个eventhandler就可以管理某一类型的所有事件。

<ul id="list">
  <li id='1'></li>
  <li id='2'></li>
  <li id='3'></li>
</ul>

如果想要给3个li添加事件监听,从事件委托的角度来看,只需要给父元素ul添加监听器,那么3格li上的事件触发的时候,都会冒泡到ul。通过检测target对象(target就是触发事件的最底层对象)上的id可以判断具体是哪个li触发的操作。

移除EventHandler

当元素绑定eventhandler时,运行中的浏览器代码与js之间会建立一个连接。连接越多,页面执行越慢。

  • 当通过纯粹的DOM操作,如removeChild()replaceChild(),或者在使用innerHTML替换页面中某一部分,绑定了eventhandler的元素被innerHTML删除了,会导致原来添加到元素中的eventhandler很有可能不被当作垃圾回收。
<div id="myDiv">
  <input type="button" value="click me" id="myBtn"/>
</div>

<script>
   var btn = document.getElementById("myBtn");
   btn.onclick = function(){
     document.getElementById("myDiv").innerHTML = "process.."
//myBtn绑定了事件,但是事件执行完被清除掉了,但是onclick绑定的事件还在
   }
</script>
  • 在卸载页面的时候可能存在空事件处理程序。
尽量使用事件委托可以减少事件绑定。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,538评论 1 11
  • JavaScript 程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发...
    劼哥stone阅读 1,278评论 3 11
  • JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬...
    LemonnYan阅读 699评论 0 4
  • 事件流 Click Me 冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标的顺序触发。触发的顺序是:di...
    醋留香阅读 825评论 0 1
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,073评论 1 10