DOM事件和事件委托

366 阅读4分钟

DOM 事件模型

事件是用户或者浏览器自己执行的某种动作,比如点击(click)按钮等,这里的click就是事件的名称。JS与html之间的交互是通过事件实现的,DOM支持大量的事件。
我们从点击事件来进行描述

事件模型

一个事件发生后,会在子元素及父元素之间进行传播(propagation),这种传播分为三个阶段。

  1. 由外向内找监听函数就是事件捕获
  2. 在目标节点触发事件
  3. 由内而外找监听函数就是事件冒泡

事件传播的最上层对象是window,上例的事件传播顺序,在捕获阶段依次为window、document、html、body、父节点、目标节点,在冒泡阶段依次为目标节点、父节点、body、html、document、window。

image.png

代码如下

<div class="爷爷">
    <div class="爸爸">
      <div class="儿子">
        文本
      </div>
    </div>
  </div>

有这样的一个网页,即 .爷爷>.爸爸>.儿子,分别添加事件监听 fnYe / fnBa / fnEr

提问1: 点击了谁?
点击文字, 算不算点击儿子
点击文字, 算不算点击爸爸
点击文字, 算不算点击爷爷
答: 都算

提问2: 调用顺序? 点击文字最先调用fnYe/fnBa/fnEr中的哪一个函数?
答: 都行. IE5调用顺序为fnEr->fnBa->fnYe, 网景调用顺序为fnYe->fnBa->fnEr

因此W3C在2002年发布了标准, 文件名为DOM Level 2 Events Specification, 规定浏览器同时支持两种调用顺序.首先按爷爷->爸爸->儿子顺序看有没有函数监听, 然后按儿子->爸爸->爷爷顺序看有没有函数监听.

因此, 用专业术语来说这2种顺序分别就是DOM事件模型的事件捕获和事件冒泡.一个事件发生后,会在子元素和父元素之间传播(propagation)。

由外向内找监听函数, 叫事件监听.

由内向外找监听函数, 叫事件冒泡.

其中文字(示例代码中)就是事件监听和事件冒泡的目标.因此DOM事件模型分为3个阶段:

(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段(示例代码中简化为: 爷爷->爸爸->儿子);
(2)目标阶段:真正的目标节点正在处理事件的阶段;(示例代码中: 文字)
(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段(示例代码中简化为: 儿子->爸爸->爷爷)。

  • 事件捕获,是指按 爷爷=>爸爸=>儿子 的顺序看有没有函数监听
  • 事件冒泡,是指按 儿子=>爸爸=>爷爷 的顺序看有没有函数监听

从外向内找监听函数,叫* 事件捕获,从内向外找监听函数,叫事件冒泡

取消冒泡和阻止默认事件

 取消冒泡

  1. event.stopPropagation()可中断冒泡,浏览器不再向上走。

【W3C标准event.stopPropagation();但不支持IE9以下版本】

【IE独有(谷歌也实现了)event.cancelBubble = true;】

function stopBubble(e) { 
        if(e && e.stopPropagation) { //非IE 
          e.stopPropagation(); 
        } else { //IE 
          window.event.cancelBubble = true; 
        } 
      } 

阻止默认事件

  1. 默认事件——表单提交,a标签跳转,右键菜单等
  2. 方法:
return false;以对象属性的方式注册的时间才生效
event.preventDefault()W3C标注,IE9以下不兼容
event.returnValue = false;兼容IE
/假定有链接<a href="http://baidu.com/" id="testA" >baidu.com</a>
var a = document.getElementById("testA");
a.onclick =function(e){
    if(e.preventDefault){
        e.preventDefault();
    }else{
        window.event.returnValue == false;
    }
}

事件委托

什么是事件委托?

事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件。我用取快递来解释这个现象: 有三个同事预计会在周一收到快递。为签收快递,委托给前台代为签收。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台也会在收到寄给新员工的快递后核实并代为签收。

应用案例

案例1:当我们写代码时,要监听某个div里面的一百个buttom按钮的监听事件,该如何快捷操作呢?
答:给多个按钮添加监听事件,监听他们的祖先即可。

案例2:当我们写代码时,要监听一个在未来创建的元素的监听事件,该怎么操作呢?
答:给不存在的元素添加监听事件,监听他们的祖先啊。

综合上面两个的案例的优点:省监听数;可以监听动态元素