事件冒泡与事件捕获


事件冒泡事件捕获分别由微软网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题
考虑下面这段代码,就不写html->head,body之类的代码了,自行脑补

<div id="outer">
    <p id="inner">Click me!</p>
</div>

上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?

为了解决这个问题微软和网景提出了两种几乎完全相反的概念。

事件冒泡

微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象

因此在事件冒泡的概念下在p元素上发生click事件的顺序应该是p -> div -> body -> html -> document

事件捕获

网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件捕获会从最外层开始发生,直到最具体的元素

因此在事件捕获的概念下在p元素上发生click事件的顺序应该是document -> html -> body -> div -> p

借用一张图:

addEventListener的第三个参数

网景 和 微软 曾经的战争还是比较火热的,当时, 网景主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡。

addEventListener的第三个参数就是为冒泡和捕获准备的.

addEventListener有三个参数:

event.addEventListener(event, function, useCapture);

第一个参数是需要绑定的事件
第二个参数是触发事件后要执行的函数
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数

冒泡的案例:

<div id="s1">s1
    <div id="s2">s2</div>
</div>
<script>
    s1.addEventListener("click",function(e){
        console.log("s1 冒泡事件");
    },false);
    s2.addEventListener("click",function(e){
        console.log("s2 冒泡事件");
    },false);
</script>

当我们点击s2的时候,执行结果如下:

http://runjs.cn/detail/kj4jgpli

捕获的案例:

<div id="s1">s1
    <div id="s2">s2</div>
</div>
<script>
    s1.addEventListener("click",function(e){
        console.log("s1 捕获事件");
    },true);
    s2.addEventListener("click",function(e){
        console.log("s2 捕获事件");
    },true);
</script>

当我们点击s2的时候,执行结果如下:

http://runjs.cn/detail/c3mjulm0

事件捕获vs事件冒泡:

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。

这里记被点击的DOM节点为target节点

  1. document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行

  2. 到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则捕获事件和冒泡事件的注册顺序,先注册先执行)

  3. target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发

总结下就是:

  • 对于非target节点则先执行捕获在执行冒泡

  • 对于非target节点则先执行冒泡在执行捕获
  • 对于target节点则是先执行先注册的事件(例如点击事件),无论冒泡还是捕获

<div id="s1">s1
    <div id="s2">s2</div>
</div>
<script>
s1.addEventListener("click",function(e){
        console.log("s1 冒泡事件");
},false); // 4
s2.addEventListener("click",function(e){
        console.log("s2 冒泡事件");
},false); // 2

s1.addEventListener("click",function(e){
        console.log("s1 捕获事件");
},true); // 1

s2.addEventListener("click",function(e){
        console.log("s2 捕获事件");
},true); // 3
</script>

http://runjs.cn/detail/fkq3uyqh

这里大体分析下执行结果
点击s2,click事件从document->html->body->s1->s2(捕获前进)
这里在s1上发现了捕获注册事件,则输出"s1 捕获事件"
到达s2,已经到达目的节点,
s2上注册了冒泡和捕获事件,先注册的冒泡后注册的捕获,则先执行冒泡,输出"s2 冒泡事件"
再在s2上执行后注册的事件,即捕获事件,输出"s2 捕获事件"
下面进入冒泡阶段,按照s2->s1->body->html->documen(冒泡前进)
在s1上发现了冒泡事件,则输出"s1 冒泡事件"

事件冒泡与事件捕获应用:事件代理

在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。

<ul id="color-list">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</ul>
(function(){
    var color_list = document.getElementById('color-list');
    var colors = color_list.getElementsByTagName('li');
    for(var i=0;i<colors.length;i++){
        colors[i].addEventListener('click',showColor,false);
    };
    function showColor(e){
        var x = e.target;
        console.log("The color is " + x.innerHTML);
    };
})();

利用事件流的特性,我们只绑定一个事件处理函数也可以完成:

(function(){
    var color_list = document.getElementById('color-list');
    color_list.addEventListener('click',showColor,false);
    function showColor(e){
        var x = e.target;
        if(x.nodeName.toLowerCase() === 'li'){
            console.log('The color is ' + x.innerHTML);
        }
    }
})();

http://runjs.cn/detail/pvsbglwc

使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。假如上述列表元素当中添加了其他的元素(如:a、span等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。

冒泡还是捕获?

对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。

IE浏览器兼容

object.attachEvent(event, function)

给元素添加事件

//添加事件---------------------------------------
function addEvent(obj,type,fn){
    if(obj.addEventListener){
        obj.addEventListener(type,fn,false);
    }else if(obj.attachEvent){
        obj.attachEvent("on"+type,fn)
    }
}

给元素删除事件

//删除事件---------------------------------------
function removeEvent(obj,type,fn){
    if(obj.removeEventListener){
        obj.removeEventListener(type,fn,false);
    }else{
        obj.detachEvent("on"+type,fn);
    }
}

获取事件源对象(事件捕获)

function getTarget(evt){
    if(evt.target){
        return evt.target;
    }else if(window.event.srcElement){
        return window.event.srcElement;
    }
}
//事件源的获取------------------------
function ele(evt){ // 捕获当前事件作用的对象
    evt = evt || window.event;
    return (obj=event.srcElement ? event.srcElement : event.target);
}

阻止冒泡兼容(事件冒泡)

function stopEvent(e){
    var evt = e || event;
    if( evt.stopPropagation ){
        evt.stopPropagation();
    }else{
        window.event.cancelBubble = true;
    }
}

事件委托兼容

function entrust(e){
    var evt = e || event;
    var target = e.srcElement || e.target;
    if (target.tagName == "*") {
              alert();
        }
}

随便看看就行了。。。。。。

javascript --- 事件冒泡与事件捕获的更多相关文章

  1. javascript中的事件冒泡、事件捕获和事件执行顺序

    谈起JavaScript的 事件,事件冒泡.事件捕获.阻止默认事件这三个话题,无论是面试还是在平时的工作中,都很难避免. DOM事件标准定义了两种事件流,这两种事件流有着显著的不同并且可能对你的应用有 ...

  2. JavaScript 进阶教程一 JavaScript 中的事件流 - 事件冒泡和事件捕获

    先看下面的示例代码: <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Jav ...

  3. 【转载】浅谈事件冒泡与事件捕获 - javascript 事件代理

    原文:https://segmentfault.com/a/1190000000749838 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发 ...

  4. JavaScript(3)---事件冒泡、事件捕获

    JavaScript(3)---事件冒泡与事件捕获 一.理解冒泡与捕获 假设有这么一段代码 <body> <div><p>标签</p> </div ...

  5. 【JavaScript】事件捕获、事件冒泡与事件委托

    2018年12月18日 最近在学习js时,遇到了三个名词:事件捕获.事件冒泡.事件委托. 一.事件捕获和事件冒泡 事件捕获和事件冒泡是为了解决网页中的事件流(事件发生的顺序)而提出的概念. 事件捕获是 ...

  6. JavaScript事件冒泡和事件委托

    JavaScript事件冒泡和事件委托 付建宇 - 2 条评论 接触JavaScript不久,学的东西也不是特别多.小雨就是习惯把平时学到的东西拿出来分享.一方面加强自己的印象,一方面可以让自己的经验 ...

  7. javascript的事件冒泡,阻止事件冒泡和事件委托, 事件委托是事件冒泡的一个应用。

    首先,弄明白js 当中,什么是事件,事件模型在js中是如何设计的.什么是事件冒泡? 什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层.这时,从最底层冒出了一个气泡,气泡会 ...

  8. JavaScript中的事件冒泡?事件传播的解释

    注:本文来源  可译网 事件冒泡是你在学习javaScript旅途中遇到的一个术语,它涉及到当一个元素被另一个元素嵌套时调用事件处理的顺序,并且两个元素注册了同一个事件(例如,点击事件). 但是事件冒 ...

  9. 浅谈js的事件冒泡和事件捕获

    本文地址:https://www.cnblogs.com/christineqing/p/7607113.html 前言:    这篇文章起源于上次工作上的原因,在事件上出的bug,所以就抽空写出一篇 ...

随机推荐

  1. 单机redis 主从实例

    环境windows xp sp3 1.redis 安装 redis windows安装文件下载地址:http://code.google.com/p/servicestack/wiki/RedisWi ...

  2. python一

    一安装工具 pip easy_install import os print (os.getcwd()) 1. 自带package和外部package      1.1 自带package举例: os ...

  3. 递归查询树形结构的SQL

    最近项目中要递归树形,案例如下: 测试数据: ),PID ),Name )) ',NULL ,'山东省' ','烟台市' ','招远市' ','青岛市' ',NULL ,'四会市' ','清远市' ' ...

  4. Unicode中文和特殊字符的编码范围

    编程中有时候需要用到匹配中文的正则,一般用 [ \u4e00-\u9fa5]+ 即可搞定.不过这正则对一般的火星文鸟语就不太适用了,甚至全角的标点符号都不包含在内.例如游戏里面的玩家名,普通青年一般都 ...

  5. WinForm 简单蒙版实现控件遮盖

    在Web上面要实现一个遮罩层或者说是蒙版吧,有了DIV那不算什么难事,只要给div定好位置和大小,把颜色的Alpha值设一下就有透明的效果.不过在Winform中实现起来就没那么简单了事.尝试过用一个 ...

  6. RabbitMQ入门教程——发布/订阅

    什么是发布订阅 发布订阅是一种设计模式定义了一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象.这个主题对象在自身状态变化时,会通知所有的订阅者对象,使他们能够自动更新自己的状态. 为了描述这种 ...

  7. C#编程总结(五)多线程带给我们的一些思考

    C#编程总结(五)多线程带给我们的一些思考 如有不妥之处,欢迎批评指正. 1.什么时候使用多线程? 这个问题,对于系统架构师.设计者.程序员,都是首先要面对的一个问题. 在什么时候使用多线程技术? 在 ...

  8. 基于吉日嘎底层架构的通用权限管理Web端UI更新:参考DTcms后台界面

    经一周的研究学习,看了国内的H+.HUI等,国外的PaperDashboardPro.Make.Metronic BootStrap等,最终选定用一个轻量的,适合中国人的,来自DTcms的后台管理UI ...

  9. Winform控件重写

    Winform控件重写 因为最近的项目中越来越多的遇到了比较特殊的一些控件,有时候我们自己封装一下可能更加方便我们的使用,下面是我们项目中用到的,简单做一个记录. TextBox控件重写 主要的控制代 ...

  10. Excel导入数据库脚本

    --数据库中不存在需要导入的表 SELECT * INTO tab_PurchasePriceTemp FROM OPENROWSET( 'Microsoft.Jet.OLEDB.4.0', 'EXC ...