本文转自:http://blog.csdn.net/luanlouis/article/details/23927347

1. 事件

在浏览器客户端应用平台,基本生都是以事件驱动的,即某个事件发生,然后做出相应的动作。

浏览器的事件表示的是某些事情发生的信号。事件的阐述不是本文的重点,尚未了解的朋友,可以访问W3school教程 进行了解,这将有助于更好地理解以下的内容 。

2. 冒泡机制

什么是冒泡呢? 下面这个图片大家应该心领神会吧,气泡从水底开始往上升,由深到浅,升到最上面。在上升的过程中,气泡会经过不同深度层次的水。

相对应地:这个气泡就相当于我们这里的事件,而水则相当于我们的整个dom树;事件从dom 树的底层 层层往上传递,直至传递到dom的根节点。

简单案例分析

下面通过一个简单的例案例来阐述冒泡原理:

定义一个html, 里面有三个简单的dom 元素:div1,div2, span,div2包含span,div1 包含div2;而它们都在body 下:

1 <span style="font-family:Microsoft YaHei;font-size:10px;">
2 <body id="body">
3 <div id="box1" class="box1">
4 <div id="box2" class="box2">
5 <span id="span">This is a span.</span>
6 </div>
7 </div>
8 </body>
9 </span>
1 <span style="font-family:Microsoft YaHei;font-size:10px;"><body id="body">
2 <div id="box1" class="box1">
3 <div id="box2" class="box2">
4 <span id="span">This is a span.</span>
5 </div>
6 </div>
7 </body></span>

界面原型如下:

在这个基础上,我们实现下面的功能:

a. body添加 click 事件监听,当body捕获到event事件时,打印出事件发生的时间和 触发事件的节点信息:

 1 <script type="text/javascript">
2 window.onload = function() {
3 document.getElementById("body").addEventListener("click",eventHandler);
4 }
5 function eventHandler(event) {
6 console.log("时间:"+new Date(event.timeStamp)+" 产生事件的节点:" + event.target.id +" 当前节点:"+event.currentTarget.id);
7 }
8 </script>

当我们依次点击"This is span",div2,div1,body后,输出以下信息:

分析以上的结果:

无论是body,body 的子元素div1,还是 div的子元素div2,还有 span, 当这些元素被点击click时,都会产生click事件,并且body都会捕获到,然后调用相应的事件处理函数。就像水中的气泡从底往上冒一样,事件也会往上传递。

事件传递的示意图如下所示:

一般地,事件在传递过程中会有一些信息,这些是事件的组成部分:事件发生的时间+事件发生的地点+ 事件的类型+事件的当前处理者+其他信息,

完整的html代码如下:

 1 <span style="font-family:Microsoft YaHei;font-size:10px;">
2 <!DOCTYPE html>
3 <html>
4 <head>
5 <meta charset="UTF-8">
6 <script type="text/javascript" src="js/jquery-1.11.0.js">
7 </script>
8 <title>Insert title here</title>
9 <style type="text/css">
10 .box1 {
11 order: green 40px solid;
12 width: 300px;
13 height: 300px;
14 margin: auto;
15 }
16
17 .box2 {
18 border: yellow 40px solid;
19 width: 220px;
20 height: 220px;
21 margin: auto;
22 }
23
24 span {
25 position: relative;
26 left: 50px;
27 top: 50px;
28 background-color: rgba(128, 128, 128, 0.22);
29 }
30 </style>
31
32 <script type="text/javascript">
33 window.onload = function() {
34 document.getElementById("body").addEventListener("click",eventHandler);
35 }
36 function eventHandler(event) {
37 console.log("时间:"+new Date(event.timeStamp)+" 产生事件的节点:" + event.target.id +" 当前节点:"+event.currentTarget.id);
38 }
39 </script>
40
41 </head>
42 <body id="body">
43 <div id="box1" class="box1">
44 <div id="box2" class="box2">
45 <span id="span">This is a span.</span>
46 </div>
47 </div>
48 </body>
49 </html>
50 </span>
 1 <span style="font-family:Microsoft YaHei;font-size:10px;"><!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <script type="text/javascript" src="js/jquery-1.11.0.js"></script>
6 <title>Insert title here</title>
7 <style type="text/css">
8 .box1 {
9 border: green 40px solid;
10 width: 300px;
11 height: 300px;
12 margin: auto;
13 }
14
15 .box2 {
16 border: yellow 40px solid;
17 width: 220px;
18 height: 220px;
19 margin: auto;
20 }
21
22      span {
23 position: relative;
24 left: 50px;
25 top: 50px;
26 background-color: rgba(128, 128, 128, 0.22);
27 }
28 </style>
29
30 <script type="text/javascript">
31 window.onload = function() {
32 document.getElementById("body").addEventListener("click",eventHandler);
33 }
34 function eventHandler(event) {
35 console.log("时间:"+new Date(event.timeStamp)+" 产生事件的节点:" + event.target.id +" 当前节点:"+event.currentTarget.id);
36 }
37 </script>
38
39 </head>
40 <body id="body">
41 <div id="box1" class="box1">
42 <div id="box2" class="box2">
43 <span id="span">This is a span.</span>
44 </div>
45 </div>
46 </body>
47 </html>48 </span>

b. 终止事件的冒泡

我们现在想实现这样的功能,在div1 点击的时候,弹出 "你好,我是最外层div。",点击div2 的时候,弹出 "你好,我是第二层div";点击span 的时候,弹出"您好,我是span。"。

由此我们会有下面的JavaScript片段:

 1 <span style="font-family:Microsoft YaHei;font-size:10px;">
2 <script type="text/javascript">
3 window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){
4 alert("您好,我是最外层div。");
5 });
6 document.getElementById("box2").addEventListener("click",function(event){
7 alert("您好,我是第二层div。");
8 });
9 document.getElementById("span").addEventListener("click",function(event){
10 alert("您好,我是span。");
11 });
12 }
13 </script>
14 </span>

预期上述代码会单击span 的时候,会出来一个弹出框 "您好,我是span。" 是的,确实弹出了这样的对话框:

然而,不仅仅会产生这个对话框,当点击确定后,会依次弹出下列对话框:

        

这显然不是我们想要的! 我们希望的是点谁显示谁的信息而已。为什么会出现上述的情况呢? 原因就在于事件的冒泡,点击span的时候,span 会把产生的事件往上冒泡,作为父节点的div2 和 祖父节点的div1也会收到此事件,于是会做出事件响应,执行响应函数。现在问题是发现了,但是怎么解决呢?

方法一:我们来考虑一个形象一点的情况:水中的一个气泡正在从底部往上冒,而你现在在水中,不想让这个气泡往上冒,怎么办呢?——把它扎破!没了气泡,自然不会往上冒了。类似地,对某一个节点而言,如果不想它现在处理的事件继续往上冒泡的话,我们可以终止冒泡:

在相应的处理函数内,加入  event.stopPropagation()   ,终止事件的广播分发,这样事件停留在本节点,不会再往外传播了。修改上述的script片段:

 1 <span style="font-family:Microsoft YaHei;font-size:10px;">
2 <script type="text/javascript">
3 window.onload = function() {
4 document.getElementById("box1").addEventListener("click",function(event){
5 alert("您好,我是最外层div。");
6 event.stopPropagation();
7 });
8 document.getElementById("box2").addEventListener("click",function(event){
9 alert("您好,我是第二层div。"); event.stopPropagation();
10 });
11 document.getElementById("span").addEventListener("click",function(event){
12 alert("您好,我是span。");
13 event.stopPropagation();
14 });
15 }
16 </script>
17 </span>

经过这样一段代码,点击不同元素会有不同的提示,不会出现弹出多个框的情况了。

方法二:事件包含最初触发事件的节点引用 和 当前处理事件节点的引用,那如果节点只处理自己触发的事件即可,不是自己产生的事件不处理。event.target 引用了产生此event对象的dom 节点,而event.currrentTarget 则引用了当前处理节点,我们可以通过这 两个target 是否相等。

比如span 点击事件,产生一个event 事件对象,event.target 指向了span元素,span处理此事件时,event.currentTarget 指向的也是span元素,这时判断两者相等,则执行相应的处理函数。而事件传递给 div2 的时候,event.currentTarget变成 div2,这时候判断二者不相等,即事件不是div2 本身产生的,就不作响应处理逻辑。

 1 <span style="font-family:Microsoft YaHei;font-size:10px;">
2 <script type="text/javascript">
3 window.onload = function() {
4 document.getElementById("box1").addEventListener("click",function(event){
5 if(event.target == event.currentTarget)
6 {
7 alert("您好,我是最外层div。");
8 }
9 });
10 document.getElementById("box2").addEventListener("click",function(event){
11 if(event.target == event.currentTarget)
12 {
13 alert("您好,我是第二层div。");
14 }
15 });
16 document.getElementById("span").addEventListener("click",function(event){
17 if(event.target == event.currentTarget)
18 {
19 alert("您好,我是span。");
20
21 }
22 });
23 }
24 </script>
25 </span>

比较:

从事件传递上看:方法一在于取消事件冒泡,即当某些节点取消冒泡后,事件不会再传递;方法二在于不阻止冒泡,过滤需要处理的事件,事件处理后还会继续传递;

优缺点:

方法一缺点:为了实现点击特定的元素显示对应的信息,方法一要求每个元素的子元素也必须终止事件的冒泡传递,即跟别的元素功能上强关联,这样的方法会很脆弱。比如,如果span 元素的处理函数没有执行冒泡终止,则事件会传到div2 上,这样会造成div2 的提示信息;

方法二缺点:方法二为每一个元素都增加了事件监听处理函数,事件的处理逻辑都很相似,即都有判断 if(event.target == event.currentTarget),这样存在了很大的代码冗余,现在是三个元素还好,当有10几个,上百个又该怎么办呢?

还有就是为每一个元素都有处理函数,在一定程度上增加逻辑和代码的复杂度。

我们再来分析一下方法二:方法二的原理是 元素收到事件后,判断事件是否符合要求,然后做相应的处理,然后事件继续冒泡往上传递;

既然事件是冒泡传递的,那可不可以让某个父节点统一处理事件,通过判断事件的发生地(即事件产生的节点),然后做出相应的处理呢?答案是可以的,下面通过给body 元素添加事件监听,然后通过判断event.target 然后对不同的target产生不同的行为。

将方法二的代码重构一下:

 1 <script type="text/javascript">
2 window.onload = function() {
3 document.getElementById("body").addEventListener("click",eventPerformed);
4 }
5 function eventPerformed(event) {
6 var target = event.target;
7 switch (target.id) {
8 case "span":
9 alert("您好,我是span。");
10 break;
11 case "div1":
12 alert("您好,我是第二层div。");
13 break;
14 case "div2":
15 alert("您好,我是最外层div。");
16 break;
17 }
18 }
19 </script>

结果会是点击不同的元素,只弹出相符合的提示,不会有多余的提示。

通过以上方式,我们把本来每个元素都要有的处理函数,都交给了其祖父节点body 元素来完成了,也就是说,span,div2,div1 将自己的响应逻辑委托给body,让它来完成相应逻辑,自己不实现相应逻辑,这个模式,就是所谓的事件委托。

下面是一个示意图:

解析Javascript事件冒泡机制(转) 本文转自:http://blog.csdn.net/luanlouis/article/details/23927347的更多相关文章

  1. 解析Javascript事件冒泡机制

    本资源引自: 解析Javascript事件冒泡机制 - 我的程序人生 - 博客频道 - CSDN.NET http://blog.csdn.net/luanlouis/article/details/ ...

  2. 解析Javascript事件冒泡机制(转)

    本文转自:http://blog.csdn.net/luanlouis/article/details/23927347 1. 事件 在浏览器客户端应用平台,基本生都是以事件驱动的,即某个事件发生,然 ...

  3. Win32消息循环机制等【转载】http://blog.csdn.net/u013777351/article/details/49522219

    Dos的过程驱动与Windows的事件驱动 在讲本程序的消息循环之前,我想先谈一下Dos与Windows驱动机制的区别: DOS程序主要使用顺序的,过程驱动的程序设计方法.顺序的,过程驱动的程序有一个 ...

  4. Oracle RAC 全局等待事件 gc current block busy 和 gc cr multi block request 说明--转载(http://blog.csdn.net/tianlesoftware/article/details/7777511)

    一.RAC 全局等待事件说明 在RAC环境中,和全局调整缓存相关的最常见的等待事件是global cache cr request,global cache busy和equeue. 当一个进程访问需 ...

  5. 从源码解析Nginx对 Native aio支持_运维_youbingchen的博客-CSDN博客 https://blog.csdn.net/youbingchen/article/details/51767587

    从源码解析Nginx对 Native aio支持_运维_youbingchen的博客-CSDN博客 https://blog.csdn.net/youbingchen/article/details/ ...

  6. Socket的用法——NIO包下SocketChannel的用法 ———————————————— 版权声明:本文为CSDN博主「茶_小哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ycgslh/article/details/79604074

    服务端代码实现如下,其中包括一个静态内部类Handler来作为处理器,处理不同的操作.注意在遍历选择键集合时,没处理完一个操作,要将该请求在集合中移除./*模拟服务端-nio-Socket实现*/pu ...

  7. Javascript事件冒泡机制

    1. 事件 在浏览器客户端应用平台,基本生都是以事件驱动的,即某个事件发生,然后做出相应的动作. 浏览器的事件表示的是某些事情发生的信号.事件的阐述不是本文的重点,尚未了解的朋友,可以访问W3scho ...

  8. JavaScript事件冒泡机制和阻止事件冒泡及默认事件

    一.阻止事件冒泡: 1.html中加return false 2.js中加return false 3.IE下:window.event.cancelBubble = true:  FF下:event ...

  9. JavaScript在IE和Firefox(火狐)的不兼容问题解决方法小结 【转】http://blog.csdn.net/uniqer/article/details/7789104

    1.兼容firefox的 outerHTML,FF中没有outerHtml的方法. 代码如下: if (window.HTMLElement) { HTMLElement.prototype.__de ...

随机推荐

  1. java-内部类访问特点-私有成员内部类-静态成员内部类-局部内部类访问局部变量

    1.内部类访问特点: - 内部类可以直接访问外部类的成员,包括私有. - 外部类要访问内部类的成员,必须创建对象. - 外部类名.内部类名 对象名 = 外部类对象.内部类对象: - 例: class ...

  2. 【vue】遇到的问题

    [一]项目编译的时候报错 npm install npm WARN @mtfe/thrift@2.3.7 requires a peer of thrift@0.11.0 but none is in ...

  3. Redis源码剖析--列表t_list实现

    Redis中的列表对象比较特殊,在版本3.2之前,列表底层的编码是 ziplist 和 linkedlist 实现的, 但是在版本3.2之后,重新引入了一个 quicklist 的数据结构,列表的底层 ...

  4. 什么是pytorch(1开始)(翻译)

    Deep Learning with PyTorch: A 60 Minute Blitz 作者: Soumith Chintala 部分翻译:me 本内容包含: 在高级层面理解pytorch的ten ...

  5. 系统限制和选项limit(一)

    从shell中获取系统限制和选项 终端输入getconf value [pathname] [root@bogon code]# getconf ARG_MAX 2097152 [root@bogon ...

  6. 数学的语言 化无形为可见 (Keith Devlin 著)

    第一章 数字为何靠的住 (已看) 第二章 心智的模式 (已看) 第三章 动静有数 (已看) 第四章 当数学成型 (已看) 第五章 数学揭开美之本质 (已看) 第六章 当数学到位 (已看) 第七章 数学 ...

  7. super and this

    super 指向父类的一个指针, 引用父类中的属性,方法或者构造函数 public class Father { String name ; Father(String myName){ name = ...

  8. js 数字数组按大小排序

    var num = [7,8,6,5,2] //倒序排列 num.sort((a,b)=>{return b-a}) //正序排列 num.sort((a,b)=>{return a-b} ...

  9. MongoDB之 写安全(Write Concern)

    MongoDB Write Concern,简称MongoDB写入安全机制,是一种客户端设置,用于控制写入安全的级别.Write Concern 描述了MongoDB写入到mongod单实例,副本集, ...

  10. nginx 镜像使用说明

    nginx 镜像说明 目录 说明 /etc/nginx nginx安装目录 /usr/share/nginx/html nginx网站资源存放的目录 运行nginx容器,相关命令: 命令 说明 doc ...