好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

DOM 事件流

DOM 事件流

DOM 事件流描述了 DOM 时间响应的阶段、路径。

DOM 事件流也会被称为 DOM 事件模型。

1. 事件流阶段

事件流有三个阶段:

捕获阶段 从window开始,寻找触发事件最深层的节点,过程中如果有节点绑定了对应事件,则触发事件 目标阶段 找到事件触及的最深节点 冒泡阶段 从最深节点按照捕获的路径进行返回,过程中如果有节点绑定了对应事件,则触发事件

现代浏览器 默 认都会在冒泡阶段触发事件。

通过 一个 例子来简单的感受一下。

    < style  >   
    . Box     { 
      dis play  :  flex ; 
     align-items  :  center ; 
     justify-content  :  center ; 
   } 

    .size-100    { 
     width  :  px ; 
     height  :  px ; 
     background  :   #4caf50  ; 
   } 

    .size-200    { 
     width  :  px ; 
     height  :  px ; 
     background  :  chocolate ; 
   } 

    .size-300    { 
     width  :  px ; 
     height  :  px ; 
     background  :  wheat ; 
   } 
    </ style  >  

   < div   class   =  "  Box  size-300 "   >  
     < div   class   =  "  Box  size-200 "   >  
       < div   class   =  "  Box  size-100 "   >  
       </ div  >  
     </ div  >  
   </ div  >  

   < div   class   =  " result "   >     </ div  >  

   < script  >   
   var   Box es  =  document . @H_545_ 301 @querySelectorAll  (  '. Box '  )  ; 
   var  result  =  document . @H_545_ 301 @querySelector  (  '.result'  )  ; 

   Box es . @H_545_ 301 @forEach  (  function  (  Box  )   { 
     Box  . @H_545_ 301 @addEventListener  (  'click'  ,   function  (  )   { 
       var  el  =  document . @H_545_ 301 @createElement  (  'p'  )  ; 

      el . innerText  =   '现在触发点击事件的是'   +   this  . className ; 

      result . @H_545_ 301 @appendChild  ( el )  ; 
     }  )  ; 
   }  )  ; 
    </ script  >  
 

点击后,观察 输出 可以发现,事件是点击到的最深层次的节点开始向上执行的。

即从 size-100 到 size-200 到 size-300 ,这就是冒泡的过程。

如果想让事件在捕获阶段就执行,可以传递 addEventListener 方法 第三个参数。

2. addEventListener 的第三个参数

addEventListener 的第三个参数用来决定事件在冒泡阶段触发还是在捕获阶段触发,其为 一个 布尔值,传递 false 则事件会在冒泡阶段触发,传递 true 则会在捕获阶段触发。

    < style  >   
    . Box     { 
      dis play  :  flex ; 
     justify-content  :  center ; 
     align-items  :  center ; 
   } 

    .ele1    { 
     background  :  wheat ; 
     width  :  px ; 
     height  :  px ; 
   } 

    .ele2    { 
     background  :  yellowgreen ; 
     width  :  px ; 
     height  :  px ; 
   } 
    </ style  >  

   < div   class   =  "  Box  ele1 "   >  
     < div   class   =  "  Box  ele2 "   >     </ div  >  
   </ div  >  

   < div   class   =  " result "   >     </ div  >  

   < script  >   
 var  ele1  =  document . @H_545_ 301 @querySelector  (  '.ele1'  )  ; 
 var  ele2  =  document . @H_545_ 301 @querySelector  (  '.ele2'  )  ; 
 var  result  =  document . @H_545_ 301 @querySelector  (  '.result'  )  ; 

 function  @H_545_ 301 @getElement  ( content )   { 
   var  el  =  document . @H_545_ 301 @createElement  (  'p'  )  ; 

  el . innerText  =  content ; 

   return  el ; 
 } 

ele1 . @H_545_ 301 @addEventListener  (  'click'  ,   function  (  )   { 
  result . @H_545_ 301 @appendChild  ( @H_545_ 301 @getElement  (  '我是元素ele1'  )  )  ; 
 }  )  ; 

ele2 . @H_545_ 301 @addEventListener  (  'click'  ,   function  (  )   { 
  result . @H_545_ 301 @appendChild  ( @H_545_ 301 @getElement  (  '我是元素ele2'  )  )  ; 
 }  )  ; 
    </ script  >  
 

根据 默 认浏览器事件是在冒泡阶段触发的规则,上述例子会先触发子节点 .ele2 的事件,再触发 .ele1 的事件。

如果想让 .ele1 在捕获阶段就触发事件,则在绑定事件的时候传递第三个参数为 true 即可。

    < style  >   
    . Box     { 
      dis play  :  flex ; 
     justify-content  :  center ; 
     align-items  :  center ; 
   } 

    .ele1    { 
     background  :  wheat ; 
     width  :  px ; 
     height  :  px ; 
   } 

    .ele2    { 
     background  :  yellowgreen ; 
     width  :  px ; 
     height  :  px ; 
   } 
    </ style  >  

   < div   class   =  "  Box  ele1 "   >  
     < div   class   =  "  Box  ele2 "   >     </ div  >  
   </ div  >  

   < div   class   =  " result "   >     </ div  >  

   < script  >   
 var  ele1  =  document . @H_545_ 301 @querySelector  (  '.ele1'  )  ; 
 var  ele2  =  document . @H_545_ 301 @querySelector  (  '.ele2'  )  ; 
 var  result  =  document . @H_545_ 301 @querySelector  (  '.result'  )  ; 

 function  @H_545_ 301 @getElement  ( content )   { 
   var  el  =  document . @H_545_ 301 @createElement  (  'p'  )  ; 

  el . innerText  =  content ; 

   return  el ; 
 } 

ele1 . @H_545_ 301 @addEventListener  (  'click'  ,   function  (  )   { 
  result . @H_545_ 301 @appendChild  ( @H_545_ 301 @getElement  (  '我是元素ele1'  )  )  ; 
 }  ,   true  )  ; 

ele2 . @H_545_ 301 @addEventListener  (  'click'  ,   function  (  )   { 
  result . @H_545_ 301 @appendChild  ( @H_545_ 301 @getElement  (  '我是元素ele2'  )  )  ; 
 }  )  ; 
    </ script  >  
 

这样 .ele1 的事件就会在捕获阶段触发。

3. 不符合W3C标准的事件流

早期的 IE 和 netscape Navigator 是不符合标准的。

前者是使用事件冒泡流,后者使用事件捕获流。

前面的章节有提到过 0级DOM事件 ,其提供的绑定事件的方式是不能指定事件触发的阶段的,其原因是在那个阶段下,还没有现在制定的 DOM 事件流 。

当时并没有统一的标准, 0级DOM事件 也并不是一套官方出台的标准,所有相关内功全部由浏览器厂商决定。

后来 W3C 很好的整合了这两种模型,便有了现在的 DOM 事件流。

4. 冒泡的终点元素

这个问题其实经常会在面试中被问到,通常题目会是这样的:

请描述一下事件捕获和冒泡的具体流程

其实问的是事件从那个节点开始捕获,然后到目标节点,最后又在哪个节点冒泡结束。

大部分面试者会回答 document ,其实根据事件对象的 path 属性 就可以得到答案。

path 属性 会返回事件冒泡的路径,其最后是到 window 对象才停止的。

其实这点在 标准 中也有描述。

注意:path 属性 有兼容性问题,可以通过 can i use 确定。可以用标准中的 compose dpath 代替。

5. 小结

开发过程中很少会取改变事件触发的阶段。但是事件流的概念依然重要,因为很多时候要阻止事件冒泡。

理解了事件流,可以理解事件委托的原理,事件委托相关的 内容 可以参阅事件相关的 性能 优化。

事件相关的优化 ? ?DOM 事件对象

查看更多关于DOM 事件流的详细内容...

  阅读:39次

上一篇

下一篇

第1节:什么是 JavaScript    第2节:学习环境准备    第3节:调试方案    第4节:JavaScript 变量    第5节:JavaScript 数据类型    第6节:JavaScript if 语句    第7节:for 语句    第8节:JavaScript 算数运算符    第9节:JavaScript 比较运算符    第10节:JavaScript 逻辑运算符    第11节:JavaScript 函数    第12节:JavaScript 表达式    第13节:JavaScript 对象    第14节:JavaScript 字符串    第15节:JavaScript 数字    第16节:JavaScript 数组    第17节:JavaScript switch 语句    第18节:JavaScript while 语句    第19节:JavaScript 的 break 与 continue    第20节:JavaScript with    第21节:document.cookie    第22节:JavaScript Function    第23节:JavaScript Math    第24节:JavaScript Date    第25节:JavaScript RegExp    第26节:JavaScript JSON    第27节:什么是 DOM    第28节:DOM 和 JavaScript 的关系    第29节:获取和操作 DOM 节点    第30节:DOM 与事件    第31节:DOM 事件绑定    第32节:DOM 事件对象    第33节:DOM 事件流    第34节:事件相关的优化    第35节:自定义事件    第36节:表单校验    第37节:什么是 BOM    第38节:常用的 BOM 相关对象    第39节:BOM 常用属性和方法    第40节:AJAX    第41节:异常处理    第42节:三元运算符    第43节:逗号操作符    第44节:void    第45节:typeof    第46节:delete 操作符    第47节:debugger    第48节:getter & setter    第49节:new 运算符与构造函数    第50节:JavaScript 原型    第51节:JavaScript instanceof    第52节:JavaScript this    第53节:严格模式    第54节:作用域    第55节:闭包    第56节:变量提升    第57节:对象包装器    第58节:Lodash    第59节:moment    第60节:swiper    第61节:ECMAScript 6    第62节:Node.js    第63节:Babel    第64节:CSS 预处理器    第65节:代码规范    第66节:TypeScript    第67节:WebComponents    第68节:Vue、React、Angular    第69节:小程序    第70节:JavaScript 关键字    第71节:分号导致的问题    第72节:对象属性访问问题    第73节:this 使用问题    第74节:浮点数精度问题    第75节:独一无二的 NaN    第76节:避免全局污染    第77节:控制台观察对象问题    第78节:根据环境选择语言特性    第79节:相关资源