浏览器事件冒泡或者捕获相关

此文用于快速回顾,不适合用来入门。

事件流(事件捕获或者冒泡相关)

建议看:博客园谢灿勇 js事件流

每次用addEventListener这个api,你需要选择绑定事件在冒泡阶段还是捕获阶段。(通常设置为false即为冒泡) 而这个api就是基于事件流的原理。

  • 事件流的本质:比如在页面上点击一个元素,这是一个点击事件。事实上,被点击的元素及其所有祖先元素都是能感知到的,只不过是,如果你没有绑定事件,它们就不会对应地触发事件响应。
  • 事件流的过程:事件流被分为三个阶段捕获过程、目标过程、冒泡过程。event对象的属性中,表示事件流中“某元素的某个被触发的事件”是绑定在哪个阶段的属性是eventPhase
  • 一个完整的JS事件流是从window开始,最后回到window的一个过程(有些浏览器是到body)
    • 也就是从window开始,依次触发绑定在捕获过程的(有绑定的)事件响应,然后是目标过程的(不分冒泡还是捕获按注册顺序),再然后是依次触发绑定在冒泡过程的直到回到window
    • 而目标阶段非常特别,目标阶段是按注册顺序决定的(也就是假如先绑定冒泡,则冒泡早于捕获得到响应) 1 #面试题

并不是所有事件都支持冒泡 #局限性

1、最适合使用事件冒泡/事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress

2、某些事件类型天生就不支持事件冒泡!不支持事件冒泡就更谈不上事件委托了。

  • blur: 在元素失去焦点时触发,该事件不支持冒泡
  • focus: 在元素获得焦点时触发,该事件不支持冒泡
  • mouseenter: 当鼠标移入元素时触发,该事件不支持冒泡
  • mouseleave: 当鼠标移出元素时触发,该事件不支持冒泡

3、mousemove、mouseover、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合于事件委托的???https://juejin.im/post/58fd95bcac502e0063b197db

mouseover的定义:当鼠标移入某元素时触发,移入和移出其子元素时也会触发。

如何应用:冒泡或者捕获事件 在事件绑定的时候

IE8及以下都不支持addEventListener,他们用attachEvent被称为IE事件处理程序;IE9及以后都支持addEventListener和attachEvent。

用addEventListener,在实战的时候,往往需要配合判断target和阻止冒泡。注意:currentTarget属性返回事件正在执行的监听函数所绑定的节点,而target属性返回事件的实际目标节点。

所以对于现代浏览器,可以同时用addEventListener绑定事件在冒泡和捕获阶段。(第三个参数通常设置为false即为冒泡)

我们知道,如果要封装兼容IE事件处理程序attachEvent和addEventListener:因为attachEvent没有第三个参数,也就是说attachEvent只支持监听冒泡,所以为了统一,在使用addEventListener的时候第三参数传递 false。

function stopPropagation(e) {
  var e = e || window.event;
  if(window.event) {       //这是IE浏览器
      e.cancelBubble = true;//阻止冒泡事件
  }else if(e && e.stopPropagation) {     //这是其他浏览器
      e.stopPropagation();//阻止冒泡事件
  }
}

事件处理程序的Event对象 IE兼容

首先作为基础,要稍微了解一下浏览器js的四种事件处理程序

摘自:于江水

IE 中往回调函数中传递的事件对象与标准有一些差异,你需要使用 window.event 来获取事件对象。所以你通常会写出下面代码来获取事件对象:

event = event || window.event

此外还有一些事件属性有差别,比如比较常用的 event.target 属性,IE 中没有,而是使用 event.srcElement 来代替。如果你的回调函数需要处理触发事件的节点,那么需要写:

target = event.srcElement || event.target;

常见的就是这点,更细节的不再多说。在概念学习中,我们没必要为不标准的东西支付学习成本;在实际应用中,类库已经帮我们封装好这些兼容性问题。可喜的是 IE 浏览器现在也开始不断向标准进步。

最后总结一下就是:

// 兼容性处理
var event = e || window.event;
// 获取到目标阶段指向的元素
var target = event.target || event.srcElement;
// 获取当前触发的事件处理函数所绑定的元素
var currentTarget = event.currentTarget;

提取冷知识:

  • 为了统一多种事件处理程序(有些它不支持捕获),一般来说都是利用冒泡比较顺手。
    • 举例:DOM Level 0即onclick等等不支持捕获:比如,onclick只支持冒泡。(PS:基本全部浏览器都支持DOM Level 0也就是onclick这些api。)
  • 绑定多个事件的效果是:onclick只能绑定一个事件,即同一个元素的后面的绑定会覆盖前面的绑定。addEventListener是先绑定先触发,老浏览器的attachEvent则相反。(IE9、10浏览器是按正序执行的,而IE8-浏览器则是按倒序执行的

事件代理 你所需要知道的

优点:

  • 减少了事件的注册,节省内存。
  • 在新增/移除DOM时,节省了的事件绑定/解绑。

使用事件代理主要有两个优势:

  1. 减少事件绑定,提升性能。之前你需要绑定一堆子节点,而现在你只需要绑定一个父节点即可。减少了绑定事件监听函数的数量。
  2. 动态变化的 DOM 结构,仍然可以监听。以前,当一个 DOM 动态创建之后,不会带有任何事件监听,除非你重新执行事件监听函数;而使用事件监听无须担忧这个问题。

缺点:

好像没有缺点,妥善的管理和合理运用能够规避问题。

实战技巧,合理运用

  • 通常需要就近委托或者不需要委托:
    • 如果层级过多,或者绑定在靠近底层的元素上,冒泡过程中,可能会被某层阻止掉。
    • 理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在ul上代理li,而不是在document上代理li。
  • 考虑因项目协作可能引发的冲突:
    • 把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。

如何利用原生实现事件代理

如果想学代码封装实现 事件代理 可以看这篇

如果使用原生的方式实现事件代理,需要注意过滤非目标节点,可以通过 id、class 或者 tagname 等等,例如:

element.addEventListener('click', function(event) {
    // 判断是否是 a 节点
    if ( event.target.tagName == 'A' ) {
        // a 的一些交互操作
    }
}, false);

vue的事件冒泡和事件代理

  • vue在组件间不冒泡,在同一个组件内的事件是可以冒泡的。
    • (如果,以后能够成功实现组件间冒泡,那么就可以通过冒泡或事件代理来实现“跨组件通信”了)
  • 在vue里面,如果想阻止冒泡用的是.stop修饰符。
文章目录
  1. 事件流(事件捕获或者冒泡相关)
    1. 并不是所有事件都支持冒泡 #局限性
  • 如何应用:冒泡或者捕获事件 在事件绑定的时候
    1. 事件处理程序的Event对象 IE兼容
      1. 提取冷知识:
  • 事件代理 你所需要知道的
    1. 优点:
    2. 缺点:
    3. 实战技巧,合理运用
    4. 如何利用原生实现事件代理
  • vue的事件冒泡和事件代理