整理之DOM事件阶段

本文主要解决的问题:

  1. 事件流
  2. DOM事件流的三个阶段

先理解流的概念

在现今的JavaScript中随处可见。比如说React中的单向数据流,Node中的流,又或是今天本文所讲的DOM事件流。都是流的一种生动体现。用术语说流是对输入输出设备的抽象。以程序的角度说,流是具有方向的数据。

事件流分事件冒泡与事件捕获

在浏览器发展的过程中,开发团队遇到了一个问题。那就是页面中的哪一部分拥有特定的事件?

可以想象画在一张纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的其实不是一个圆,而是纸上所有的圆。放到实际页面中就是,你点击一个按钮,事实上你还同时点击了按钮所有的父元素。
开发团队的问题就在于,当点击按钮时,是按钮最外层的父元素先收到事件并执行,还是具体元素先收到事件并执行?所以这儿引入了事件流的概念。

通俗说,事件流所描述的就是从页面中接受事件的顺序。

因为有两种观点,所以事件流也有两种,分别是事件冒泡和事件捕获。现主流是事件冒泡。

事件冒泡

事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。

下面写个例子:

 1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Event Bubbling</title>
6 </head>
7 <body>
8 <button id="clickMe">Click Me</button>
9 </body>
10 </html>

然后,我们给button和它的父元素,加入点击事件。

 1 var button = document.getElementById('clickMe');
2
3 button.onclick = function() {
4 console.log('1. You click Button');
5 };
6 document.body.onclick = function() {
7 console.log('2. You click body');
8 };
9 document.onclick = function() {
10 console.log('3. You click document');
11 };
12 window.onclick = function() {
13 console.log('4. You click window');
14 };

在Chrome浏览器运行:

如果点击了button,那么这个点击事件会按如下的顺序传播

  1. button
  2. body
  3. document
  4. window

也就是说,click事件首先在<button>元素上发生,然后逐级向上传播。

事件捕获

事件捕获的概念,与事件冒泡正好相反。它认为当某个事件发生时,父元素应该更早接收到事件,具体元素则最后接收到事件。比如说刚才的demo,如果是事件捕获的话,事件发生顺序会是这样的:

  1. window
  2. document
  3. body
  4. button

事件阶段(Event Phases)

当一个DOM事件触发时,它不是在触发的对象上只触发一次的,而是经历三个阶段。分别为

1:一开始从文档的根节点流向目标对象(捕获阶段)

2:然后在目标对向上被触发(目标阶段)

3:之后再回溯到文档的根节点(冒泡阶段).

1. 事件捕获阶段

事件的第一个阶段是捕获阶段。事件从文档的根节点出发,随着DOM树的结构向事件的目标节点流去。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主要任务是建立传播路径,在冒泡阶段,事件会通过这个路径回溯到文档跟节点。当事件发生时,首先发生的是事件捕获,为父元素截获事件提供了机会。
例如,我把上面的Demo中,window点击事件更改为使用事件捕获模式。(addEventListener最后一个参数,true则代表使用事件捕获模式,false则表示使用事件冒泡模式。不理解的可以去学习一下addEventListener函数的使用)

1 window.addEventListener('click', function() {
2 console.log('4. You click window');
3 }, true);

此时,点击button的效果是这样的。

可以看到,点击事件先被父元素截获了,且该函数只在事件捕获阶段起作用。

处于目标与事件冒泡阶段

事件到了具体元素时,在具体元素上发生,并且被看成冒泡阶段的一部分。
随后,冒泡阶段发生,事件开始冒泡。

阻止事件冒泡

事件冒泡过程,是可以被阻止的。防止事件冒泡而带来不必要的错误和困扰。
这个方法就是:stopPropagation()
我们对button的click事件做一些改造。

1 button.addEventListener('click', function(event) {
2 // event为事件对象
3 console.log('1. You click Button');
4 event.stopPropagation();
5 console.log('Stop Propagation!');
6 }, false);

点击后,效果如下图:

不难看出,事件在到达具体元素后,停止了冒泡。但不影响父元素的事件捕获。

总结

事件流:描述的就是从页面中接受事件的顺序。

分有事件冒泡与事件捕获两种。

DOM事件流的三个阶段:

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段

参考资料:《JavaScript高级程序设计》第三版


一直没有搞懂jquery中的事件是怎么添加事件处理程序的。原来就是用了事件委托,关键就是通过冒泡方式实现在最高层(document)处理事件,通过判断事件target的id,给以不同的handler。当然也可以通过事件捕获来处理,但是因为ie中只有冒泡,所以还是用冒泡兼容性更好一些。

细细总结一下:

1.冒泡与捕获。

冒泡和捕获是两个完全不同的事件流传播方式,当点击页面上某个元素时,不知道用户是对该元素感兴趣还是对该元素的父元素该兴趣?为此提出了两种不同的事件流模型。一种是捕获,由网景提出的,首先是最不具体的元素接收到事件,然后再一级一级传播到最具体的元素。另一种是冒泡,是由ie提出的,它和捕获完全相反,首先是最具体的元素接收到事件,然后一级一级传播到最不具体的元素,一般都是document。

2.事件委托

上面的参考博文写得很清楚了。大概模式就是这样(以onclick为例子,其他事件mouseover mousemove mouseout mousedown mouseup click dbclick keyup keydown)

document.onclick=function(event){

var event=event?event:window.event;

var target=event.target?event.target:event.srcElement;

switch(target.id){

  case:'btnid1':

  btnid1handler();

  break;

  case:'btnid2':

  btnid2handler();

  break;

  }

};

3.ie事件和dom(2)事件模型

ie事件模型的事件流只包括事件冒泡,而dom(2)事件模型的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。ie8以下的版本只支持ie事件模型,ie9及其他浏览器都支持dom事件流。

区别:

(1)添加事件处理程序的不同。dom0级只能在冒泡阶段调用事件处理程序。dom2级事件模型通过addEventListener支持在冒泡和捕获阶段注册事件处理程序。而ie事件模型通过attachEvent在事件冒泡阶段注册事件处理程序。

(2)事件处理程序的event参数和this的作用域不同。addEventListener中的this就指向当前事件元素,event就是当前事件对象。而attachEvent的this指向window,event是全局对象作用域window中的event对象,但是同时也会作为参数直接传递给事件处理程序函数。但是在只支持ie事件模型的浏览器中通过dom0级方法注册的事件处理程序中的event,只能通过全局的window.event进行访问,但无论在哪个浏览器中,通过dom0级注册的事件处理程序中的this也是指向当前事件元素,可以做到很好的跨浏览器兼容。

(3) 移除事件监听。通过dom0级注册的事件处理程序,通过 element.onclick=null;这样的形式来移除注册事件。通过dom2级事件注册的时间处理程序通过removeEventListener来移除事件处理程序,而ie事件模型通过datachEvent来移除。

(4)阻止默认事件模型和阻止事件冒泡的方法。dom2级可以通过preventDefault来阻止事件的默认行为。ie通过returnValue属性为false来阻止事件的默认行为。dom2采用stopPropagation方法来阻止冒泡,而ie通过cancelBubble属性为true来阻止冒泡。

4 鼠标事件

A表示B元素的外部,B表示元素,C表示B的子元素

鼠标事件有以下几种:

mouseover:从A到B时触发,冒泡

mouseenter:从A到B时触发,并且不冒泡,DOM3级出现的事件

mouseleave:从B到A时触发,并且不冒泡,DOM3级出现的事件

mouseout:从B到A,或者从C到B时触发,冒泡

mousemove:B到B时触发

mousedown:按下任意鼠标按钮时触发

mouseup:释放鼠标按钮时触发

click:在同一个元素相继触发mousedown和mouseup才会触发click事件,任意一个被取消,都不会触发click

dbclick:连续两次click才能触发dbclick。


更多技术分享请关注 http://hist.ac.cn/

整理之DOM事件阶段、冒泡与捕获、事件委托、ie事件和dom模型事件、鼠标事件的更多相关文章

  1. 初始js闭包&事件的冒泡和捕获&EVENT对象

    一.初识闭包 function a(){   var n = 0;   this.inc = function () {     n++;     console.log(n);   }; } var ...

  2. react第五单元(事件系统-原生事件-react中的合成事件-详解事件的冒泡和捕获机制)

    第五单元(事件系统-原生事件-react中的合成事件-详解事件的冒泡和捕获机制) 课程目标 深入理解和掌握事件的冒泡及捕获机制 理解react中的合成事件的本质 在react组件中合理的使用原生事件 ...

  3. 从零开始的全栈工程师——js篇2.20(事件对象 冒泡与捕获)

    一.复习 面向对象 1)单例模式 2)工厂模式 3)构造函数 ①类js天生自带的类 基类object function array number math boolean date regexp st ...

  4. JS事件冒泡与捕获

    1事件传播——冒泡与捕获 默认情况下,事件使用冒泡事件流,不使用捕获事件流.然而,在Firefox和Safari里,你可以显式的指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这 ...

  5. Dom捕捉事件和冒泡事件-原理与demo测试

    先参考一下百度百科对冒泡事件流的解释: ----------不喜欢读文字的同学,可以直接看下面demo,传递顺序简单明了! http://baike.baidu.com/link?url=kaeJHT ...

  6. 手持设备点击响应速度,鼠标事件与touch事件的那些事

    前言 现在一直在做移动端的开发,这次将单页应用的网页内嵌入了app,于是老大反映了一个问题:app应用点击响应慢!我开始不以为然,于是拿着网页版的试了试,好像确实有一定延迟,于是开始了研究,最后选择了 ...

  7. wxpyhon 鼠标事件例子

    #encoding:utf-8 import wx import wx.aui class MyFrame(wx.Frame): def __init__(self, *args, **kwargs) ...

  8. 浅谈Qt事件的路由机制:鼠标事件

    请注意,本文是探讨文章而不是教程,是根据实验和分析得出的结果,可能是错的,因此欢迎别人来探讨和纠正. 这几天对于Qt的事件较为好奇,平时并不怎么常用,一般都是用信号,对于事件的处理,一般都是需要响应键 ...

  9. PyQt5编程:鼠标事件

    参考链接:https://www.cnblogs.com/zhuluqing/p/9028816.html 一.每个事件都被封装成相应的类: pyqt中,每个事件类型都被封装成相应的事件类,如鼠标事件 ...

  10. opencv-python教程学习系列5-处理鼠标事件

    前言 opencv-python教程学习系列记录学习python-opencv过程的点滴,本文主要介绍opencv-python处理鼠标事件,坚持学习,共同进步. 系列教程参照OpenCV-Pytho ...

随机推荐

  1. Linux基础-host文件解析

    任务目标:为集群内的机器设定主机名,利用/etc/hosts文件来解析自己的集群中所有的主机名, 相应的集群的配置应该改成使用主机名的方式 使用 hostnamectl set-hostname 设定 ...

  2. npm_一个有意思的npm包

    $ npm install yosay const yosay = require('yosay'); console.log(yosay('Hello, and welcome to my fant ...

  3. Treats for the Cows 区间DP POJ 3186

    题目来源:http://poj.org/problem?id=3186 (http://www.fjutacm.com/Problem.jsp?pid=1389) /** 题目意思: 约翰经常给产奶量 ...

  4. Hibernate5笔记2--单表的增删改查操作

    单表的增删改查操作: (1)定义获取Session和SessionFactory的工具类: package com.tongji.utils; import org.hibernate.Session ...

  5. Shell-help格式详解

    前言 linux shell命令通常可以通过-h或--help来打印帮助说明,或者通过man命令来查看帮助,有时候我们也会给自己的程序写简单的帮助说明,其实帮助说明格式是有规律可循的 帮助示例 下面是 ...

  6. PHP 生成、识别二维码及安装相关扩展/工具

    2018-02-20 00:30:26  更新:推荐新扩展(极力推荐) 这篇文章里用的两个二维码扩展都有些问题和麻烦:phpqrcode(生成二维码)的源码有点小 bug: 而 php-zbarcod ...

  7. Runtime - Associated Objects (关联对象) 的实现原理

    主要围绕3个方面说明runtime-Associated Objects (关联对象) 1. 使用场景 2.如何使用 3.底层实现 3.1  实现原理 3.2 关联对象被存储在什么地方,是不是存放在被 ...

  8. VC++ 编译libcurl 支持SSL,GZIP

    由于网上下载的 libcurl 不支持 gzip,只好自己动手编译,期间走了很多弯路,下面是最终成功的记录. 我所使用的环境 Visual Studio 2010 . Windows 7 64 bit ...

  9. 11 The Go Memory Model go语言内置模型

    The Go Memory Model go语言内置模型 Version of May 31, 2014 Introduction 介绍 Advice 建议 Happens Before 在发生之前 ...

  10. Python 内置装饰器

    内置的装饰器 ​ 内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些. @property ​ 在了解这个装饰器前,你需要知道在不使用装饰器怎么写一个属性. d ...