一、事件流(捕获,冒泡)

 
事件流:指从页面中接收事件的顺序,有冒泡流和捕获流。
当页面中发生某种事件(比如鼠标点击,鼠标滑过等)时,毫无疑问子元素和父元素都会接收到该事件,可具体顺序是怎样的呢?冒泡和捕获则描述了两种不同的顺序。
 
DOM2级事件规定事件流包括三个阶段,如图:
 

 
假如我们点击一个div, 实际上是先点击document,然后点击事件传递到div,而且并不会在这个div就停下,div有子元素就还会向下传递,最后又会冒泡传递回document,如上图
 
为了兼容更多的浏览器,非特殊情况一般我们都是把事件添加到在事件冒泡阶段。
 
二、事件处理程序
 
DOM0级事件处理程序
 
例子:
 
1 var btn5 = document.getElementById('btn5');
2 btn5.onclick=function(){
3 console.log(this.id);//btn5
4 };
 
注意:基于DOM0的事件,对于同一个dom节点而言,只能注册一个,后边注册的 同种事件 会覆盖之前注册的。利用这个原理我们可以解除事件,btn5.onclick=null;其中this就是绑定事件的那个元素;
 
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理;
 
DOM2级事件处理程序
 
DOM2支持同一dom元素注册多个同种事件,事件发生的顺序按照添加的顺序依次触发(IE是相反的)。
DOM2事件通过addEventListener和removeEventListener管理
 
//addEventListener(eventName,handlers,boolean);removeEventListener(),两个方法都一样接收三个参数,第一个是要处理的事件名,第二个是事件处理程序,第三个值为false时表示在事件冒泡阶段调用事件处理程序,一般建议在冒泡阶段使用,特殊情况才在捕获阶段;
注意:通过addEventListener()添加的事件处理程序只能用removeEventListener()来移除,并且移除时传入的参数必须与添加时传入的参数一样;比如
 
例子:
 
1 var btn2 = document.getElementById('btn2');
2 var handlers = function () {
3 console.log(this.id);
4 };
5
6 btn2.addEventListener('click',handlers,false);
7
8 btn2.removeEventListener('click',handlers.false);
 
IE事件处理程序
 
//IE事件处理程序(IE和Opera支持)
//IE用了attachEvent(),detachEvent(),接收两个参数,事件名称和事件处理程序,通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段,所以平时为了兼容更多的浏览器最好将事件添加到事件冒泡阶段,IE8及以前只支持事件冒泡;
 
例子:
 
1 var btn3 = document.getElementById('btn3');
2 var handlers2=function(){
3 console.log(this===window);//true,注意attachEvent()添加的事件处理程序运行在全局作用域中;
4 };
5 btn3.attachEvent('onclick',handlers2);
 
跨浏览器事件处理程序
 
 1 //创建的方法是addHandlers(),removeHandlers(),这两个方法属于一个叫EventUtil的对象;但是这个没有考虑到IE中作用域的问题,不过就添加和移除事件还是足够的。
2
3 var EventUtil = {
4 addHandlers: function (element, type, handlers) {
5 if (element.addEventListener) {
6 element.addEventListener(type, handlers, false);
7 } else if (element.attachEvent) {
8 element.attachEvent(on + type, handlers);
9 } else {
10 element['on' + type] = handlers;
11 }
12 },
13 removeHandlers: function (element, type, handlers) {
14 if (element.removeEventListener) {
15 element.removeEventListener(type, handlers, false);
16 } else if (element.detachEvent) {
17 element.detachEvent(on + type, handlers);
18 } else {
19 element['on' + type] = null;
20 }
21 }
22 };
 
例子:
 
1 var btn4=document.getElementById('btn4');
2 var handlers3=function(){
3 console.log('123')
4 };
5 EventUtil.addHandlers(btn4,'click',handlers3);
6 //……
7 EventUtil.removeHandlers(btn4,'click',handlers3); 
 
在同一个对象上注册事件,并不一定按照注册顺序执行,冒泡或捕获模式会影响其被触发的顺序;
 
三、事件对象
 
兼容触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含了所有与事件有关的信息,比如导致事件的元素target,事件的类型,及其他特定的相关信息。例如鼠标操作导致的事件对象中会包含鼠标的位置,单双击等,而键盘操作导致的事件对象会包含按下的键等信息;
 
事件被触发时,会默认给事件处理程序传入一个参数e , 表示事件对象;通过e,我们可以获得其中包含的与事件有关的信息;
 
只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完毕,event对象就会被销毁;
 
DOM中的事件对象
 
兼容DOM的浏览器会自动将一个事件对象event传递给事件处理程序
 
ps:关于事件对象中的this,target,currentTarget,看个例子:(注:event.target不支持IE浏览器,应该用event.srcElement;还有 IE中通过attachment添加的事件是运行在全局作用域中的,this===window)
 
当事件绑定在真正的目标元素上时,this===target===currentTarget ,而且绑定事件时是否捕获结果都是一样的,此时eventParse==2;
 
 1 //this,target,currentTarget,this===currentTarget
2 $('#outer').on('click','#center',function(e){
3 console.log(this.id);//on()中间的参数是个过滤器,相当于将事件绑定到了#center上;此时点击#center将不会触发事件
4 console.log(e.target.id);
5 console.log(e.currentTarget.id);
6 });
7
8 $('#outer').on('click',function(e){
9 console.log(this.id);
10 console.log(e.target.id);
11 console.log(e.currentTarget.id);
12 });
13
14 event.stopPropagation()不能简单说阻止了事件的冒泡,其实也阻止了事件的继续捕获,确切的说应该是阻止事件的进一步传播
15
16 var d1 = document.getElementById('d1');
17 d1.addEventListener('click', function (evt) {
18 console.log('d1');
19 evt.stopPropagation();
20 }, true);
21 var d2 = document.getElementById('d2');
22 d2.addEventListener('click', function () {
23 console.log('d2');
24 }, true);
 
输出结果是:s1;
 
event.stopPropagation()可以组织事件的传播,但它阻止不了绑定在该元素上的其他函数的执行,比如将上面例子改一下,给d1再绑定一个事件,同时d1的第一个事件中改成event.stopImmediatePropagation(),那么第二个事件也会被阻止,它不仅阻止事件的传播还阻止后续事件的执行;
 
 1 var d1 = document.getElementById('d1');
2 d1.addEventListener('click', function (evt) {
3 console.log('d1');
4 evt.stopImmediatePropagation();
5 }, true);
6 d1.addEventListener('click', function (evt) {
7 console.log('d1+1');
8 }, true);
9 var d2 = document.getElementById('d2');
10 d2.addEventListener('click', function () {
11 console.log('d2');
12 }, true);
 
IE中的事件对象
 
IE中event参数是未定的,事件对象是作为window的一个属性存在的,因此可以通过window.event来访问event对象,不同于DOM级中event是作为参数直接传入和返回;
 
事件函数是以on开头的;
 
属性上也有一些不同,如下:
 
 
跨浏览器的事件对象
 
虽然DOM和IE中对象不同,但是两者event中的全部信息和方法都是类似的只是实现方式不同,可以用前面提到过的EventUtil对象来求同存异
 
 1 var EventUtil = {
2 addHandler: function (element, type, handler) {
3 if (element.addEventListener) {
4 element.addEventListener(type, handler, false);
5 } else if (element.attachEvent) {
6 element.attachEvent(on + type, handler);
7 } else {
8 element['on' + type] = handler;
9 }
10 },
11
12 getEvent: function (event) {
13 return event ? event : window.event;
14
15 },
16
17 getTarget: function (event) {
18 return event.target || event.srcElement;
19 },
20
21 preventDefault: function (event) {
22 if (event.preventDefault) {
23 event.preventDefault();
24 } else {
25 event.returnValue = false;
26 }
27 },
28
29 stopPropagation: function (event) {
30 if (event.stopPropagation) {
31 event.stopPropagation();
32 } else {
33 event.cancelBubble = true;
34 }
35 },
36
37 removeHandler: function (element, type, handler) {
38 if (element.removeEventListener) {
39 element.removeEventListener(type, handler, false);
40 } else if (element.detachEvent) {
41 element.detachEvent(on + type, handler);
42 } else {
43 element['on' + type] = null
44 }
45 }
46
47 };
 
使用上面的EventUtil对象,举个例子:
 
1 var myBtn=document.getElementById('my-btn');
2 btn.onclick=function(event){
3 event=EventUtil.getEvent(event);
4 EventUtil.preventDefault(event);
5 };
 
个例子:必须要假设有一个事件对象event传入了事件处理程序中,要使用EventUtil中的方法需要将该事件对象传给那些方法,该事件对象则需要通过其getEvent方法来获得;
 
四、事件委托
 
每当将事件处理程序制定给元素时,运行中的浏览器代码与支持页面交互的JS代码之间就会建立一个连接,而这种连接越多,页面执行起来就越慢。考虑内存和性能问题,为了解决事件处理程序过多的问题,采用事件委托变得很有必要。(考虑到内存,也应该尽量减少不必要的事件处理程序,对于内存中过时不用的’空事件处理程序’,也是很有必要将其移除的;)
 
因为冒泡机制,比如既然点击子元素,也会触发父元素的点击事件,那我们完全可以将子元素的事件要做的事写到父元素的事件里,也就是将子元素的事件处理程序写到父元素的事件处理程序中,这就是事件委托;利用事件委托,只指定一个事件处理程序,就可以管理某一个类型的所有事件;例如:
 
 1 var myLinks=document.getElementById('my-links');
2 myHandlers=function(event){
3 event=EventUtil.getEvent(event);
4 var target=EventUtil.getTarget(event);
5
6 switch(target.id){
7 case 'item-1':
8 location.href='http://www.cnblogs.com/lazychen/';
9 break;
10 case 'item-2':
11 document.title='event';
12 break;
13 case 'item-3':
14 console.log('hi');
15 break;
16 }
17 };
18 EventUtil.addHandler(myLinks,'click',myHandlers);
 
点击任何一个 li ,该点击事件依然会冒泡到父元素 ul 上,所以直接将点击 li 后要做的事写到了父元素的点击事件里;
 
《JS高设 3rd》中列出了几种最适合采用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress
 
总结一下js委托相关的:
  • 因为把事件绑定到了父节点上,因此省了绑定事件。就算后面新增的子节点也有了相关事件,删除部分子节点不用去销毁对应节点上绑定的事件
  • 父节点是通过event.target来找对应的子节点的。(事件处理程序中的this值始终等于currentTarget的值,指向的是绑定到的那个元素)

初步理解JS的事件机制的更多相关文章

  1. js的事件机制二

    js的事件机制二 1.给合适的HTML标签添加合适的事件 onchange-----select下拉框 onload-----body标签 单双击-----用户会进行点击动作的HTML元素 鼠标事件 ...

  2. js的事件机制

    js的事件机制 解释:当我们的行为动作满足了一定的条件后,会触发某事务的执行. 内容: 1.单双击事件 单击:onclick 当鼠标单击时候会触发 双击:ondbclick 当鼠标双击时候会触发 2. ...

  3. 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...

  4. 我也来说说js的事件机制

    原文链接:http://www.w3cfuns.com/notes/17398/8062de2558ef495ce6cb7679f940ae5c.html 学js,不懂事件机制,基本可以说学了js,就 ...

  5. 一张图看懂 JS 的事件机制

    一.为什么 JavaScript 单线程 假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 为了避免复杂性, JS ...

  6. 深入理解React:事件机制原理

    目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...

  7. node.js 的事件机制

    昨天到今天, 又看了一边node 的事件模块,  觉得很神奇~  分享一下  - -> 首先, 补充下对node 的理解: nodeJs 是一个单进程单线程应用程序, 但是通过事件和回调支持并发 ...

  8. C#学习之初步理解委托、事件、匿名方法和Lambda

    最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...

  9. js内部事件机制--单线程原理

    原文地址:https://www.xingkongbj.com/blog/js/event-loop.html http://www.haorooms.com/post/js_xiancheng ht ...

随机推荐

  1. springcloud-概念

    springcloud-概念 一.架构演进过程 单体架构----分布式架构----SOA(eg.dubbo)服务治理架构----微服务 随着互联网的发展,需求的激增致使网站应用规模的扩大,最后转成了技 ...

  2. vue-mixins和vue高阶组件

    我们在开发过程中,因为需求的变更,往往会遇见对现有组件的改造和扩展. 那么我们有什么方法对现有组件进行改造和扩展呢? 常见的我们可以使用mixins方式 下面就让我们来看一下怎么使用mixins方式对 ...

  3. VM439:1 https://unidemo.dcloud.net.cn 不在以下 request 合法域名列表中,请参考

    在编写uni-app编写代码时,pc端获取数据正常,但是小程序端却出现以下的错误. 解决方法如下: 将相应的选项勾选

  4. String与C风格字符串转换

    String字符串转换为C风格字符串需要利用string类的成员函数c_str().而C风格字符串转换转换为string字符串可以直接利用运算符=.首先介绍c_str()函数原型: const val ...

  5. ARM伪指令和协处理器访问指令

    伪指令本身没有对应的机器码 .global声明全局符号,点事GUN汇编的特点 .data定义数据段 .equ DA #0x89 定义宏 .align 4 4字节对齐 mov 指令里的立即数只能是8位的 ...

  6. vue修改富文本中的元素样式

    富文本编辑器目前应用很广泛,而有时候我们想要对其中的一些元素的样式进行修改,就会遇到问题. 首先,直接修改是不可行的,因为是用v-html标签进行渲染的,无法直接获取到. 在修改的时候,一般是按标签进 ...

  7. Python修炼之路-数据类型

    Python编程之列表 列表是一个使用一对中括号"[   ]" 括起来的有序的集合,可以通过索引访问列表元素,也可以增加和删除元素. 列表的索引:第一个元素索引为0,最后一个元素索 ...

  8. HTTP协议的请求方法

    HTTP概念: HTTP是一个基于TCP/IP通信协议来传递数据,包括html文件.图像.结果等,即是一个客户端和服务器端请求和应答的标准 1.http无连接:限制每次连接只处理一个请求,服务端完成客 ...

  9. Java AtomicInteger类的使用方法详解_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Samp ...

  10. 【NOIP2014模拟8.17】Magical GCD

    题目 对于一个由正整数组成的序列, Magical GCD 是指一个区间的长度乘以该区间内所有数字的最大公约数.给你一个序列,求出这个序列最大的 Magical GCD. 分析 根据暴力的思想, \( ...