js事件流机制冒泡和捕获
JavaScript与HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
事件流
从页面中接收事件的顺序称为事件流。
IE --> 事件冒泡流
Netscape --> 事件捕获流
查看源码:DOM2事件-捕获-冒泡
事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
我们先来个简单的例子,这是HTML结构
<!DOCTYPE html>
<html lang="en">
<head>
<title>js事件流</title>
</head>
<body>
<div id="div">
我是div
</div>
</body>
</html>
有一个div元素。如果我们点击<div>
元素,那么这个click事件的顺序会怎么呢?
我们给几个元素都添加监听事件,
element.addEventListener(event, function, useCapture)
参数说明:
- event 字符串。指定事件名,比如 click、mouseenter、mouseleave
- function 函数。指定要事件触发时执行的函数。
- useCapture 布尔值。指定事件是否在捕获或冒泡阶段执行。默认值是false,即事件在冒泡阶段执行。true,在捕获阶段执行
var div = document.getElementById('div')
var body = document.body
var html = document.documentElement
div.addEventListener('click', function () {
console.log('div标签')
}, false)
body.addEventListener('click', function () {
console.log('body ')
}, false)
html.addEventListener('click', function () {
console.log('html')
}, false)
document.addEventListener('click', function () {
console.log('document')
}, false)
然后点击<div>
元素,查看控制台输出,
由上面的输出结果可以看出,这个click事件会按照如下顺序传播:
<div>
---> <body>
---> <html>
--->document
也就是说,click事件首先发生在目标元素,然后,click事件沿着DOM树向上传播到document对象。这就是事件冒泡。
所有现代浏览器都支持事件冒泡。IE9、Firefox、Chrome和Safari则将事件一直冒泡到window对象。
如果,我们在每个DOM元素上都设置监听事件,会得到的事件的传播顺序是:
<div>
---><body>
---> <html>
--->document
---> window
事件捕获
Netscape Communicator团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预期目标之前捕获它。
我们还是可以看刚才的例子,DOM结构不变,我们修改下监听事件
var div = document.getElementById('div')
var body = document.body
var html = document.documentElement
div.addEventListener('click', function () {
console.log('div标签')
}, true)
body.addEventListener('click', function () {
console.log('body ')
}, true)
html.addEventListener('click', function () {
console.log('html')
}, true)
document.addEventListener('click', function () {
console.log('document')
}, true)
之后,还是点击<div>
元素,得到的结果
从上面例子可以看出,事件走向: document
---><html>
---> <body>
---><div>
也就是说,在事件捕获过程中,document对象首先接收到click事件,然后事件沿着DOM树依次向下,一直传播到事件的实际目标,即
<div>
元素。这就是事件捕获。与事件冒泡过程,截然相反。
尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但是现代浏览器大部分都是从window对象开始捕获事件的。
如果,我们在每个DOM元素上都设置监听事件,会得到的事件的传播顺序是:
window
---> document
---><html>
---> <body>
---><div>
由于在老版本的浏览器中不支持,因此事件捕获用的人比较少,除非在特殊需要的时候才使用。
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。

事件捕获阶段:在DOM事件流中,实际的目标(
<div>
元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到<html>
再到<body>
后就停止了。处于目标阶段:事件在
<div>
上发生,并在事件处理中被看成冒泡阶段的一部分。事件冒泡阶段:冒泡阶段发生,事件又传播回文档。
虽然,“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但是IE9及以上的现代浏览器都会在捕获阶段触发事件目标对象上的事件。结果,就是有两个机会在目标对象上面操作事件,也就是说上图中的步骤4,既可以在捕获阶段发生,也可以在冒泡阶段发生。
IE8级更早版本不支持DOM事件流,现代浏览器都支持DOM事件流。
事件流的应用
事件流比较典型应用是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理一类型的所有事件。
我们查看一个常用例子,这是一个无序列表的DOM结构:
<ul>
<li id="li1">我是第1个li</li>
<li id="li2">我是第2个li</li>
<li id="li3">我是第3个li</li>
</ul>
我们的需求是,点击不同列,输出不同的消息。
- 第一种做法:给每个
<li>
添加点击事件,这样能分别处理事件,展示不同的内容。
document.getElementById('li1').addEventListener('click', function(e) {
console.log('我是第一个li')
}, false)
document.getElementById('li2').addEventListener('click', function(e) {
console.log('我是第2个li')
}, false)
document.getElementById('li3').addEventListener('click', function(e) {
console.log('我是第3个li')
}, false)
单击每一个<li>
,会输出对应的内容。
- 第二种做法:给
<li>
元素的父元素<ul>
添加一个处理事件,
document.querySelector('ul').addEventListener('click', function (e) {
console.log(e.target.innerText)
}, false)
单击每一个<li>
,会展示不同的<li>
中文本元素内容。
在这段代码中,我们只为<ul>
元素添加了一个onclick事件处理程序。由于所有<li>
都是<ul>
元素的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理。
以上两种方式,第二种所具有的优势:
事前消耗更低。因为只取得了一个DOM元素,只添加了一个事件处理程序。
占用的内存更少。每个函数都是对象,都会占用内存。
性能更优。 内存中的对象越多,性能就越差。
如果以后要增减
<li>
元素,也不用修改事件方法,可以获取相同的处理结果。
所以,比较推荐使用第二种方式。
最适合采用事件委托技术的事件包括click
、mousedown
、mouseup
、keydown
、keyup
和keypress
。虽然mouseover
和mouseout
事件也冒泡,但要适当处理它们并不容易,而且经常要计算元素的位置。
参考资料:
JavaScript高级程序设计(第三版)- 第13章 事件
查看源码:DOM2事件-捕获-冒泡
js事件流机制冒泡和捕获的更多相关文章
- Dom事件流、冒泡、捕获
Dom事件流 dom的结构是一个倒立的树状结构.当一个html元素触发事件时,事件会在dom的根节点和触发事件的元素节点之间传播,中间的节点都会收到该事件. 捕获:div元素触发事件时,事件先从根节点 ...
- js 事件流和事件冒泡阻止
js 事件流和事件冒泡阻止 事件流 当浏览器发展到第四代的时候(IE4与Netscape4)浏览器开发团队遇到一个有意思的的问题: 页面的哪一部分会拥有某个特定的事件? 比如在纸上画上一组同心圆,如果 ...
- [转]as3事件流机制彻底理解
题记: 看过网上一些as3事件流的教程,觉得大多都讲得不甚清楚,让人不能直观的理解事件流.而这篇教程以将事件流过程比喻成捕鱼过程,形象简单. 在此基础上对于as3事件流总算有了全面的理解.事件流机制说 ...
- 一次关于js事件出发机制反常的解决记录
起因:正常情况下我点击s2时是先弹出我是children,再弹出我是father,但是却出现了先弹出我是father,后弹出我是children的情况,这种情况是在和安卓app交互的h5页面中出现的, ...
- js 事件详解 冒泡
起因:正常情况下我点击s2时是先弹出我是children,再弹出我是father,但是却出现了先弹出我是father,后弹出我是children的情况,这种情况是在和安卓app交互的h5页面中出现的, ...
- JS事件流、事件监听、事件对象、事件委托
JS事件流: 01.DOM级别和DOM事件 02.JS事件流:页面中接收事件的顺序 事件冒泡阶段-->处于目标阶段-->事件捕获阶段 (事件捕获总发生在事件冒泡前面) 03.捕获:从外向里 ...
- JS事件流模型
JS事件流模型 事件捕获Event Capturing是一种从上而下的传播方式,以click事件为例,其会从最外层根节向内传播到达点击的节点,为从最外层节点逐渐向内传播直到目标节点的方式. 事件冒泡E ...
- js事件流、事件处理程序/事件侦听器
1.事件流 事件冒泡 IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档). 事件捕获 ...
- JS事件流理解
事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字. 事件是javaScript和DOM之间的桥梁. 你若触发,我便执行--事件发生,调用它的处理函数执行相 ...
随机推荐
- H3C WAP712C 路由器设置
0.做完任何设置之后都要执行保存操作,否则断电后设置会丢失! 1.默认登录参数:IP:192.168.0.50ID:adminPD:h3capadmin 2.修改默认IP地址:设备 --> 接口 ...
- [HNOI2010]城市建设
[HNOI2010]城市建设 玄学cdq O(nlog^2n)的动态最小生成树 其实就是按照时间cdq分治+剪枝(剪掉一定出现和不可能出现的边) 处理[l,r]之间的修改以及修改之后的询问,不能确定是 ...
- node服务端口被占用Error listen EADDRINUSE :::3000
Error: listen EADDRINUSE: address already in use :::3000,出现这个报错说明3000端口被占用 解决方法:找到占用该端口的程序,kill杀掉它就可 ...
- Centos环境下安装mongoDB
安装前注意: 此教程是通过yum安装的.仅限64位centos系统 安装步骤: 1.创建仓库文件: vi /etc/yum.repos.d/mongodb-org-3.4.repo 然后复制下面配置, ...
- 分享一个在线生成微信跳转链接实现微信内跳转浏览器打开URL的工具
前言 现如今微信对第三方推广链接的审核是越来越严格了,域名在微信中分享转发经常会被拦截,一旦被拦截用户就只能复制链接手动打开浏览器粘贴才能访问,要不然就是换个域名再推,周而复始.无论是哪一种情况都会面 ...
- vue.js基础
1,感谢菜鸟教程 2,第一个实例 <html> <head> <meta charset="utf-8"> <title>Vue 测 ...
- git工具——版本的创建与回退
1.创建一个版本库 进入要管理的文件路径:cd f:/ZK/Opencv3.4.2-YOLOv3 输入命令: git init 2.版本创建与回退 在文件目录下创建一个文件code.txt: vi c ...
- SQL报错盲注
嗯哼,这几天篮球比赛,天天训练,学习都耽搁了,DDCTF做了一会心态就爆炸了,蓝瘦,明天再打一场,希望能赢呢,打完就疯狂继续学习了.今天抽空又做了一些基本的SQL注入题目,墨者学院的一道报错注入的题目 ...
- Quartz.NET 入门(转)
概述 Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等. Quartz.NET允许开发人员根据时间间隔(或天)来调度作业.它实现了 ...
- 【easy】532. K-diff Pairs in an Array
这道题给了我们一个含有重复数字的无序数组,还有一个整数k,让我们找出有多少对不重复的数对(i, j)使得i和j的差刚好为k.由于k有可能为0,而只有含有至少两个相同的数字才能形成数对,那么就是说我们需 ...