基于事件冒泡、事件捕获和事件委托详解

事件冒泡、事件捕获和事件委托

在javascript里,事件委托是很重要的一个东西,事件委托依靠的就是事件冒泡和捕获的机制,我先来解释一下事件冒泡和事件捕获:

事件冒泡会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。

事件捕获会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。

这么说是不是很抽象,其实就像我敲击了一下键盘,我在敲击键盘的同时,我是不是也敲击了这台电脑,我写个例子大家就明白了:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <style type="text/css">
        #box1 { width: 300px; height: 300px; background: blueviolet; }
        #box2 { width: 200px; height: 200px; background: aquamarine; }
        #box3 { width: 100px; height: 100px; background: tomato; }
        div { overflow: hidden; margin: 50px auto; }
    </style>
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3"></div>
            </div>
        </div>
        <script>
            function sayBox3() {
                console.log('你点了最里面的box');
            }
            function sayBox2() {
                console.log('你点了最中间的box');
            }
            function sayBox1() {
                console.log('你点了最外面的box');
            }
            // 事件监听,第三个参数是布尔值,默认false,false是事件冒泡,true是事件捕获
            document.getElementById('box3').addEventListener('click', sayBox3, false);
            document.getElementById('box2').addEventListener('click', sayBox2, false);
            document.getElementById('box1').addEventListener('click', sayBox1, false);
        </script>
    </body>
</html>

我们画了三个box,结构是父子关系,分别绑定了打印事件,现在我们来点击最中间的红色box:

我们发现,我们仅仅是点击了红色的box,但是绿色和紫色的box也被触发了打印事件,触犯顺序是 红色>绿色>紫色,这种现象就是事件冒泡了。

我们再试试事件捕获,把上面代码里监听事件的第三个参数改为true,然后点击红色的box:

我们还是只点击最中间的红色box,和上一次一样,也是三个box都触发了事件,但是顺序反过来了,紫色>绿色>红色,这种现象称为事件捕获。

通过上面的例子,应该很容易就理解了事件冒泡和事件捕获,我们平时都是默认冒泡的,冒泡是一直冒到document根文档为止。

现在来谈谈事件委托,事件委托又称之为事件代理,我们通过一个通俗的例子来解释:

有三个同事预计会在周一收到快递,为了签收快递,有两种办法:1.三个人在公司门口等快递;2.委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收(可以给暂时不存在的节点也绑定上事件)。

我们再举另一个例子:

现在有一个ul,ul里又有100个li,我想给这100个li都绑定一个点击事件,我们一般可以通过for循环来绑定,但是要是有1000个li呢? 为了提高效率和速度,所以我们这时可以采用事件委托,只给ul绑定一个事件,根据事件冒泡的规则,只要你点了ul里的每一个li,都会触发ul的绑定事件,我们在ul绑定事件的函数里通过一些判断,就可以给这100li都触发点击事件了。

具体怎么实现,看代码:

// 这里不讲IE,结尾再说
function clickLi() {
    alert('你点击了li');
}
document.getElementById('isUl').addEventListener('click', function(event) {
    // 每一个函数内都有一个event事件对象,它有一个target属性,指向事件源
    var src = event.target;
    // 我们判断如果target事件源的节点名字是li,那就执行这个函数
    // target里面的属性是非常多的,id名、class名、节点名等等都可以取到
    if(src.nodeName.toLowerCase() == 'li') {
       clickLi() ;
    }
});

这样我们就通过给ul绑定一个点击事件,让所有的li都触发了函数。

那如果想给不同的li绑定不同的函数怎么办?

假设有3个li,我们先写3个不同的函数,再给3个li设置不同的id名,通过判断id名是不是就能给不同的li绑定不同的函数啦:

<body>
    <ul id="isUl">
        <li id="li01">1</li>
        <li id="li02">2</li>
        <li id="li03">3</li>
    </ul>
    <script>
        function clickLi01() {
            alert('你点击了第1个li');
        }
        function clickLi02() {
            alert('你点击了第2个li');
        }
        function clickLi03() {
            alert('你点击了第3个li');
        }
        document.getElementById('isUl').addEventListener('click', function(event) {
            var srcID = event.target.id;
            if(srcID == 'li01'){
                clickLi01();
            }else if(srcID == 'li02') {
                clickLi02();
            }else if(srcID == 'li03') {
                clickLi03();
            }
        });
    </script>
</body>

这就是所谓的事件委托,通过监听一个父元素,来给不同的子元素绑定事件,减少监听次数,从而提升速度。

那么,能不能阻止元素的事件冒泡呢,答案是可以的。

一开始那个例子,假如我们真的只想点击最里面的那个红色box,不想另外两个box的事件被触发,我们可以在给红色box绑定事件的函数里这么写:

function sayBox3(event) {
    // 阻止冒泡
    event.stopPropagation();
    console.log('你点了最里面的box');
}
document.getElementById('box3').addEventListener('click', sayBox3, false);

这样我们再点击红色的box,那就只会触发它本身的事件啦。

那阻止冒泡有没有实际用途呢?答案是有的,我们看这个例子:

这是一个模态框,现在的需求是当我们点击红色的按钮需要跳转页面,然后点击白色的对话框不需要任何反应,点其它任何地方就关闭这个模态框。

这里就需要用到阻止冒泡了,红色的按钮是白色对话框的子元素,白色对话框又是这整个模态框的子元素,我们给模态框加上一个点击事件关闭,然后给红色的按钮加上一个点击事件跳转,这时就产生了一个问题,只要点击白色的对话框,由于冒泡机制,这个模态框也会关闭,实际上我们并不想点击白色的对话框有任何反应,这时我们就给这个白色的对话框绑定一个点击事件,函数里写上event.stopPropagation();,这样就OK了。

关于IE

老版本的IE存在兼容问题,根本不支持addEventListener()的添加事件和removeEventListener()的删除事件,它有自己的监听方法:

// 添加事件,事件流固定为冒泡
attachEvent(事件名,事件处理函数)
// 删除事件
detachEvent(事件名,事件处理函数)

还有IE里的事件对象是window.event,事件源是srcElement,阻止冒泡写法也不一样:

function() {
    // IE里阻止冒泡
    window.event.cancelBubble = true;
    // IE里获取事件源的id
    var srcID = window.event.srcElement.id;
}
function(event) {
    // 非IE里阻止冒泡
    event.stopPropagation();
    // 非IE里获取事件源的id
    var srcID = event.target.id;
}

补充

关于js的浏览器兼容问题,一般用能力检测来解决,if(){}else{}

我们平时工作一般都是用jquery,IE这些特殊情况早就帮我们做好兼容啦。

jquery在1.7的版本之后,最流行的事件监听方法是$(元素).on(事件名,执行函数),它还有一种事件委托的写法$(委托给哪个元素).on(事件名,被委托的元素,执行函数)

最后说一点,如果元素被阻止冒泡了,千万别去用事件委托的方式监听事件,因为事件委托的原理是利用事件冒泡,当冒泡被阻止,就无法监听了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JavaScript事件委托原理与用法实例分析

    本文实例分析了JavaScript事件委托原理与用法.分享给大家供大家参考,具体如下: 在日常中,我们可能会听到事件委托这样的概念,有些同学可能对事件委托已经很了解了,也有些同学可能只是听过事件委托,只是会简单的使用,但是对于事件委托的原理不怎么知道.所以该博文会解释一下原生js的事件委托的原理,为什么会有事件委托,为什么可以这样用事件委托等等问题. 1. js中的事件流 在解析事件委托之前,我们先回顾一下js中的事件流,即冒泡和捕获. ① .冒泡:当下级节点触发某个事件的时候,该事件会逐级向上

  • JS 事件绑定、事件监听、事件委托详细介绍

    在JavaScript的学习中,我们经常会遇到JavaScript的事件机制,例如,事件绑定.事件监听.事件委托(事件代理)等.这些名词是什么意思呢,有什么作用呢? 事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 在DOM中直接绑定事件

  • jquery关于事件冒泡和事件委托的技巧及阻止与允许事件冒泡的三种实现方法

    首先,大家都知道,jQuery事件触发时有2种机制,一种是事件委托,另一种是事件冒泡(IE情况暂时不考虑).拿click事件做例子,先附上一段代码: html: <body> <div id="box"> <p id="btn">我是按钮</p> </div> </body> style: .hid{ display:none; } script: $('#box').click(functio

  • 浅谈事件冒泡、事件委托、jQuery元素节点操作、滚轮事件与函数节流

    一.事件冒泡定义 事件冒泡是指在一个对象触发某类事件(比如单击onclick事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,甚至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层级的最顶层,即document对象(有些浏览器是window).. 二.事件冒泡的作用 事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元

  • javascript 中的事件委托详解

    这几天看到一个面试题,大概就是,让你给1000个li都添加一个click事件,应该怎么添加?大多数人第一开始的感觉可能就是,每个li上边都添加一个呗,那要是这样的话,估计面试的时候就会GG了,这里就是撤出了我们的事件冒泡和捕获机制,以及事件委托机制,对于上边这些,我们慢慢来看. 首先说一下事件冒泡和事件捕获机制,事件冒泡是有微软公司提出来的,事件捕获是有网景公司提出来的,当时两家是争论的不可开交,后来w3c也没办法,就采取了折中的方式,事件产生后先捕获后冒泡, 通常,在js中监听事件的方法共有三

  • 基于事件冒泡、事件捕获和事件委托详解

    事件冒泡.事件捕获和事件委托 在javascript里,事件委托是很重要的一个东西,事件委托依靠的就是事件冒泡和捕获的机制,我先来解释一下事件冒泡和事件捕获: 事件冒泡会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止. 事件捕获会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止. 这么说是不是很抽象,其实就像我敲击了一下键盘,我在敲击键盘的同时,我是不是也敲击了这台电脑,我写个例子大家就明白了: <!DOCTYPE html> <ht

  • javascript事件冒泡,事件捕获和事件委托详解

    1.事件冒泡:在javascript事件传播过程中,当事件在一个元素上出发之后,事件会逐级传播给先辈元素,直到document为止,有的浏览器可能到window为止.并不是所有的事件都有冒泡现象,比如如下几个:blur事件 ,focus事件, load事件 2.事件委托:事件捕获恰好与事件冒泡相反,它从顶层祖先元素开始,直到事件触发元素. js事件捕获一般通过DOM2事件模型addEventListener来实现的: target.addEventListener(type, listener,

  • JavaScript事件代理和委托详解

    在javasript中,代理.委托经常出现. 那么它究竟在什么样的情况下使用?它的原理又是什么? 这里介绍一下javascript delegate的用法和原理,以及Dojo,jQuery等框架中delegate的接口. JavaScript事件代理 事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数. 这主要得益于浏览器的事件冒泡机制,下面我们具体举个例子来解释如何使用这个特性. 这个例子主要取

  • js事件委托详解

    1.每个函数都是对象,占用内存.内存中的对象越多,性能越差.解决事件处理过多问题的办法是事件委托. 2.事件委托冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件. 实例 <ul id="myLinks"> <li id="myLi1">text1</li> <li id="myLi2">text2</li> <li id="myLi3">text

  • Vue的事件响应式进度条组件实例详解

    写在前面 找了很多vue进度条组件,都不包含拖拽和点击事件,input range倒是原生包含input和change事件,但是直接基于input range做进度条的话,样式部分需要做大量调整和兼容性处理.即使做好了,将来需要修改外观,又是一番折腾. 基于以上两个原因,做了一个可以响应input和change事件(即一个是拖动进度条到某处,一个是在进度条某位置点击使其值变为该位置)的div实现的Vue组件,这样既满足了对进度条事件的需求,也带来了如有需求变动,样式修改很方便的好处. 效果图 以

  • node.js事件循环机制及与js区别详解

    目录 一.是什么 二.流程 三.题目 一.是什么 在浏览器事件循环(opens new window)中,我们了解到javascript在浏览器中的事件循环机制,其是根据HTML5定义的规范来实现 而在NodeJS中,事件循环是基于libuv实现,libuv是一个多平台的专注于异步IO的库,如下图最右侧所示: 上图EVENT_QUEUE 给人看起来只有一个队列,但EventLoop存在6个阶段,每个阶段都有对应的一个先进先出的回调队列 二.流程 上节讲到事件循环分成了六个阶段,对应如下: tim

  • 基于js中this和event 的区别(详解)

    今天在看javascript入门经典-事件一章中看到了 this 和 event 两种传参形式.因为作为一个初级的前端开发人员平时只用过 this传参,so很想弄清楚,this和event的区别是什么,什么情况下用什么比较合适. onclick = changeImg(this)       vs     onclick = changeImg(event) <img src='usa.gif' onclick="changeImg(event)" /> <scrip

  • 基于Bootstrap3表格插件和分页插件实例详解

    首先看下实现效果图,如果觉得还不错,请参考实现代码. 上面数据 下面分页 使用方法 1 导入bootstrap的css <link rel="stylesheet" href="css/v3/bootstrap.min.css"> 2 导入jquery <script src="js/jquery-1.10.1.min.js" type="text/javascript"></script>

  • 基于jquery的on和click的区别详解

    使用jQuery写js时,有些用on写行为函数,有些用click,弄清二者区别很重要. 以下是用于测试二者区别的HTML代码. <!DOCTYPE html> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="h

随机推荐