1、事件冒泡


要理解事件冒泡,就得先知道事件流。事件流描述的是从页面接收事件的顺序,比如如下的代码:

<body><div> click me!</div>
</body>

如果在body和div内都注册了click的事件监听,之后又点击了div区域,是body先响应还是div先响应?有意思的是,当时的浏览器开发团队IE和Netscape提出了差不多完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape提出的事件流是事件捕获流。

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。如上代码,点击click事件会这样传播:div->body->html->document(虽然我没写html元素,但是页面上默认还是会存在的)

现代的所有浏览器都支持事件冒泡,但还是有些细微差别。IE5.5以及更早版本中的事件冒泡会跳过<html>元素(从body直接跳到document)。IE9、ff、chrome和safari则将事件一直冒泡到window对象。

2、事件捕获


Netscape团队则提出另一种事件流-事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,如果仍以上面的代码举例:document->html->body->div。

虽然事件捕获是Netscape唯一支持的事件流模型,但是IE9、Safari、chrome、opera和ff目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。

因为老版本的浏览器不支持事件捕获,所以我们建议使用事件冒泡。

3、DOM事件流


“DOM2级事件”规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。还是上面的代码作为例子,单击div元素会按照如下顺序触发事件:document->html->body->div->body->html->document。

在DOM事件流中,实际的目标(div)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件到body就停止了,下一个阶段是“处于目标”阶段,于是事件在div上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。但是多数支持DOM事件流的浏览器都实现了一种特定的行为:即使“DOM2级事件”规范明确要求捕获阶段不会涉及目标事件,但IE9、safari、chrome、ff和opera9.5及更高版本都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上面操作。(IE9、opera、ff、chrome和Safari都支持DOM事件流,IE8及更早版本不支持DOM事件流)。

4、事件处理程序


响应某个事件的函数就叫做事件处理程序

DOM0级的事件处理程序很简单,onclick就是常用的DOM0级事件处理函数,只会在冒泡阶段被处理。

而“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()removeEventListener(),所有DOM节点都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用。DOM2级方法添加事件处理程序的好处是可以添加多个事件处理程序,会按照添加顺序被处理(无论是捕获还是冒泡)。这也是为什么DOM0级事件兼容各种浏览器,我们却还是要使用DOM2的原因之一。

var div = document.getElementById('myDiv');
div.addEventListener('click', function() {console.log(this.id);
}, true);div.addEventListener('click', function() {console.log('hello world');
}, true);

而IE与DOM不同,它有自己的方法:attachEvent()detachEvent(),这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段(所以不需要第三个参数)。

var div = document.getElementById('myDiv');
div.attachEvent('onclick', function() {console.log('hello world');
});

注意第一个参数是onclick,而非DOM标准的click。在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域,在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行,而在使用attachEvent()方法的情况下,事件处理程序在全局作用域中运行,因此this等于window(这点要特别注意!!!)。attachEvent()也能添加多个事件处理程序,但是事件的执行顺序和添加顺序相反

5、跨浏览器的事件处理程序


因为浏览器之间的差异(其实就是IE大家都懂的),所以需要编写跨浏览器的事件处理程序。

var EventUtil = {addHandler: function(element, type, handler) {if (element.addEventListener) {  // DOM2element.addEventListener(type, handler, false);} else if (element.attachEvent) {  // IEelement.attachEvent('on' + type, handler);} else {  // DOM0element['on' + type] = handler;}},removeHandler: function(element, type, handler) {if (element.removeEventListener) {element.removeEventListener(type, handler, false);} else if (element.detachEvent) {element.detachEvent('on' + type, handler);} else {element['on' + type] = null;}}
};

6、事件对象


在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息。坑爹的是DOM中的事件对象和IE又有不同的玩法。

先来说说DOM中的:

var div = document.getElementById('myDiv');
div.onclick = function(e) {console.log(e.type);
};div.addEventListener('click', function(e) {console.log(e.type);
}, false);

上面代码我们应该都不陌生,分别实现了DOM0级和DOM2级的事件对象。

e有很多的属性和方法,这里提几个常用的。targetcurrentTarget,target指的是事件的真正目标,而currentTarget指的是当前的目标,正是利用target我们可以做事件代理

要阻止特定事件的默认行为,我们可以使用preventDefault()方法,例如链接的默认行为就是在被单击时会导航到其href指定的url,如果你想阻止这个默认行为,那么通过链接的onclick事件处理程序可以取消它:

var link = document.getElementById('myLink');
link.onclick = function(e) {e.preventDefault();
};

只有cancelable属性设置为true的事件,才可以使用preventDefault()来取消其默认行为。

另外,stopPropagation()方法用于立即停止事件在DOM层中的传播,即取消进一步的事件捕获或冒泡。

var div = document.getElementById('myDiv');
div.onclick = function(e) {console.log('click!');e.stopPropagation();
};document.body.onclick = function(e) {console.log('hello world');
};

而IE中的事件对象是这么用的:

var div = document.getElementById('myDiv');
div.onclick = function() {var e = window.event;console.log(e.type);
};div.attachEvent('onclick', function(e) {// 也可以通过window.e访问console.log(e.type);
});

IE中的event对象也有很多属性和方法,比如srcElement就是和DOM中的target属性相同,而returnValue属性相当于DOM中的preventDefault()方法,它们的作用都是取消给定事件的默认行为。只要将该值设置为false,就可以阻止默认行为。相应地,canceBubble属性和DOM中的stopPropagation()方法作用相同,因为IE只支持冒泡,所以它只能取消事件冒泡。

跨浏览器的事件对象:

var EventUtil = {getEvent: function(e) {return e ? e : window.event;},getTarget: function(e) {return e.target || e.srcElement;},preventDefault: function(e) {if (e.preventDefault) {e.preventDefault();} else {e.returnValue = false;}},stopPropagation: function(e) {if (e.stopPropagation) {e.stopPropagation()} else {e.cancelBubble = true;}}
}

7、事件委托


有了以上作为基础,事件委托应该是很简单了。什么是事件委托?对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click事件会冒泡到document层次,也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

举个经常举的例子,比如有如下代码:

<ul id='myLink'><li id='a'> apple </li><li id='b'> banana </li><li id='c'> orange </li>
</ul>

需要的效果是每点击相应的<li>选项,alert它里面的单词,或许很简单:

var lis = document.getElementsByTagName('li');
for(var i = 0, len = lis.length; i < len; i++) {lis[i].onclick = function() {alert(this.innerHTML);};
}

但是如上代码绑定了三个事件,我们知道每个事件绑定都需要占用一定的内存,更糟糕的是,如果在代码执行过程中,动态地又添加了一个li,这时它没有绑定click的事件,我们还需要手动添加!这时,我们就可以用到事件委托技术:

var f = document.getElementById('myLink');
f.onclick = function(e) {console.log(e.target.innerHTML);
};

好吧,就是这么简单!

JavaScript高级程序设计笔记 事件冒泡和事件捕获相关推荐

  1. JavaScript高级程序设计笔记01 | 第一章到第四章 | 关键字与保留字 | 数据类型 | 操作符 | 作用域

    观前提示:大部分都是书上的内容,个人理解的部分的较少,看我的笔记还不如去看书 第二章 async:可选.表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或 等待加载其他脚本.只对外部 ...

  2. javascript高级程序设计--笔记01

    概述 JavaScript的实现包含三个部分: 1  核心(ECMAScript)   提供核心语言功能 2  文档对象模型(DOM)  一套提供了访问以及操作网页内容的API 3  浏览器对象模型( ...

  3. JavaScript高级程序设计笔记 - 第四章 变量 作用域 内存问题

    4.1 基本类型和引用类型的值 基本类型: 简单的数据段 引用类型: 指那些可能有多个值构成的对象, 指保存在内存中的对象 4.1.2 复制变量值 除了保存的方式不同之外,在从一个变量向另一个变量复制 ...

  4. javascript高级程序设计笔记

    1.要讲一个值转换成其对应的Boolean类型 ,可以调用转型函数Boolean(). var message="hello world!"; var messageAsBoole ...

  5. 读《Javascript高级程序设计》中的javascript事件处理程序(事件侦听器)心得

    今天读了<Javascript高级程序设计>中的javascript事件处理程序(事件侦听器)部分的内容,总结一些自己的心得: 事件就是用户或者浏览器自身执行的某种动作.例如click . ...

  6. 《JavaScript高级程序设计》读书笔记 【8章~】【持更】

    文章目录 上一篇:<JavaScript高级程序设计>读书笔记 [1~7章] 8. BOM 8.1. window对象 窗口位置 窗口大小 打开窗口 间歇调用与超时调用 系统对话框 8.2 ...

  7. 《JavaScript高级程序设计》学习笔记(一)

    文章目录 一.什么是JavaScript JavaScript的组成 二.HTML中的JavaScript 2.1 <script>元素 2.1.1 标签位置 2.1.2推迟执行脚本 2. ...

  8. javascript高级程序设计学习笔记

    javascript高级程序设计,当枕头书已经好久了~zz  现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...

  9. JavaScript:事件冒泡和事件委托

    2019独角兽企业重金招聘Python工程师标准>>> JavaScript事件代理和委托(Delegation) JavaScript事件冒泡和事件委托 JavaScript:通过 ...

最新文章

  1. struts配置文件没有标签提示
  2. 理解与理论:人工智能基础问题的悲观与乐观
  3. java 大端字节序_理解字节序 大端字节序和小端字节序
  4. C语言如何产生随机数
  5. Spring boot优点
  6. android sdk platform-tools 19.0.1,如何强制Android Studio 0.6.0使用SDK Build Tools 19.1.0
  7. 小学计算机课教学设计,小学信息技术教学设计三篇
  8. Spring Data JPA 从入门到精通~@Query详解
  9. 自定义标签处理器类的生命周期
  10. opencv 的norm_OpenCV:norm-范数求解函数
  11. 数字类型转换以及函数全介绍
  12. 第三章 基于QT和DCMTK的Dicom 图像浏览器---单个Dicom图像读取类
  13. 零基础学Python课后实战第五章
  14. 内存屏障(Memory Barrier)(一)什么是写屏障?
  15. python笔记003
  16. Rockchip RK3588 kernel dts解析之音频模块
  17. Android 系统广播(大全)
  18. GAMES101 作业0 环境配置 超详细小白教程
  19. 机器学习数学基础九:回归分析
  20. 干货:O2O美团外卖四部曲

热门文章

  1. 学习scala03 控制结构
  2. 深入学习typedef和typename
  3. js的数组和对象的多种复制和清空, 以及区分JS数组和对象的方法
  4. 毛坯房验房留意事项及细节有哪些呢?
  5. [FPGA] 2、新建并运行一个工程
  6. AD RMS高可用(二)为rms群集服务器申请证书
  7. Oracle分组取前n条记录
  8. 闪修侠、极客修、千机网,手机维修选哪家?
  9. ftp,http YUM库
  10. prototype、JQuery中跳出each循环的方法