JavaScript:事件:冒泡和捕获机制

更多
2020年07月11日 21点44分 作者:叶飞 修改

一般的事件处理不需要考虑这种情况。但是,这不仅是一个常见面试题,而且有其实际使用场景。


演示准备

  1. 有父子两个元素
        <div id="propagate">
            <p>
                源栈欢迎您
            </p>
        </div>
    为了便于演示,加上一点CSS效果:
        <style>
            #propagate {
                padding: 30px;
                border: 1px solid;
            }
    
                #propagate > p {
                    background-color: blue;
                    border: 1px dashed;
                }
        </style>
  2. 都注册了事件
    var father = document.getElementById('propagate'),
        child = father.children[0];
    father.addEventListener('click', function (event) {
        console.log('event handler on father');
    });
    child.addEventListener('click', function () {
        console.log('event handler on child');
    });
  3. 用户点击在子元素上

@想一想@

  1. 这是不是也同时点在了父元素上?
  2. 所以,是不是一次事件就会调用两个handler?
  3. 那么,先触发的是子元素,还是父元素呢?
演示:略


冒泡和捕获

浏览器大战(复习)的年代,不同的浏览器使用了不同的机制来处理这种场景

  • 冒泡(bubble):事件从内向外传递,先触发子元素事件,再触发父元素的
  • 捕获(capture):事件从外向内传递,先触发父元素事件,再触发子元素的

现代浏览器都按W3C标准进行了统一。

当事件行为发生时,有两个“传播”阶段:首先由外向内“捕获”,然后再从内向外“冒泡”,如下图所示。


addEventListener()控制

那为什么上述演示的结果感觉是只有“冒泡”呢?

因为我们在绑定事件时(默认)指明了:该事件处理程序只适用/响应于冒泡阶段。

addEventListener()还有第三个参数useCapture,当其为

  • false:事件绑定在冒泡阶段(默认)
  • true:事件绑定在捕获阶段


演示:

father.addEventListener('click', function (event) {
    console.log('event handler on father');
}, true);
child.addEventListener('click', function () {
    console.log('event handler on child');
}, true);
以及:父true子false,父false子true


理解:上述(由浏览器控制的)①②③④四个阶段始终是存在的,我们能控制的是:事件处理程序(handler)在哪一个阶段(phase)予以响应。


两个Target

现在我们来看一个真实的适用场景:给一个父元素里面的所有子元素绑定click事件。

    <div id="propagate">
        <p>
            源栈欢迎您
        </p>
        <p>
            大神小班
        </p>
        <p>
            灵活学制
        </p>
    </div>

要求显示用户所点击DOM元素(如何确定?能不能用this?)的文本。

实际上,这里面可以使用的有三个对象:

  • event.currentTarget(等同于this):JavaScript代码中绑定事件的DOM元素
  • event.target:(用户)点击的DOM元素


father.addEventListener('click', function (event) {
    console.log('this === event.target:' + (this === event.target));
    console.log('event.target:' + event.target.innerHTML);  //符合题意需求
    console.log('this === event.currentTarget:' + (this === event.currentTarget));
    console.log('event.currentTarget:' + event.currentTarget.innerHTML);
});


@试一试@:当在父子元素上都绑定了事件之后,又各自使用冒泡/捕获,会是一种什么情形?


stopPropagation()

可以想象,事件冒泡/捕获机制极有可能会带来胡乱,所以有时候我们需要:阻止事件(行为)继续向上传递,这就需要调用:

    event.stopPropagation();


注意:propagation包含冒泡和捕获

演示:采用冒泡/捕获,在父子元素事件handler中添加stopPropagation()


常见面试题

:stopPropagation()和preventDefault()的区别?

  • 联系:都是event方法,都能阻止事件发生
  • 区别:
    • preventDefault()阻止的DOM元素默认的(不是我们JavaScript开发人员绑定的)事件,如提交按钮的提交,超链接的跳转……
    • stopPropagation()阻止的不是事件本身,是事件的传播,通过阻止事件传播阻止JavaScript开发人员的绑定事件


作业

  1. 用代码和PPT演示:事件的冒泡和排序传播

JavaScript 事件 冒泡 捕获
赞: 3 踩: 0

打赏
已收到打赏的 帮帮币

你的 打赏 非常重要!
为了保证文章的质量,每一篇文章的发布,都已经消耗了作者 1 枚 帮帮币
没有“帮帮币”,作者无法发布新的文章。

全系列阅读
评论 / 0

前端基础


HTML和CSS

HTML最常用的标签和属性,含HTML5的语义标签和新属性,但不包含需要JavaScript操作的HTML5 API,以及CSS基础(简述CSS 3动画相关),以及bootstrap.js以外的Bootstrap的内容。

Javascript基础和JQuery

Javascript的基本语法,JQuery类库(含Ajax),以及Bootstrap的JavaScript组件部分

进阶和ES6

借助于ES6,讲解JavaScript中一些更复杂的语法特性,如作用域、闭包、面向对象、原型链、this变化、module等

全部
关键字



帮助

反馈