学生党关于js事件冒泡机制和事件捕获机制的简单理解

546 阅读6分钟

#关于事件冒泡和事件捕获的个人理解

html相当于页面的框架,css控制了页面的样式,js控制了页面的种种行为和操作,通过js来实现和html的交互。js是相当重要的,它就相当于人类的大脑,执行指令、代码做出更重要的事,使网页显的有活力。

js无比重要,重要的原因个人感觉是因为是事件的存在,事件增强了页面的可操作性,方便用户体验和操作页面,传递更多信息,给了用户更好的体验。举个例子,没事件的存在,你甚至不能用谷歌搜索,百度搜索查东西,那你就很难找到你想要的资料,极不方便。作为开发人员,我们需要学会使用js事件执行代码增强用户的体验。毕竟写代码最终用来服务大众。

元素执行事件需要绑定,由此才知道是哪个元素做了什么事件。

本人是个学生,学编程没多久,最近学到了冒泡和捕获,感觉很有意思,就想发表自己的看法,这也是我第一次在掘金发表文章,望大佬多多指点与批评,使我对代码的认知越来越高。

冒泡和捕获并不是一个公司提出的。 冒泡是由微软提出的,顾名思义呢,就像水里的水泡,从水底冒到水面。代码的事件冒泡类似,从触发事件的元素,开始向上传播,该元素的子元素的祖先带的同种事件都被会被触发。以下是个极其简单的例子。

<!DOCTYPE html>
<html lang="en" class="jiaoSpan">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body class="guSpan">
    <section class="waiSpan">
        <p class="outsetSpan">
            <div class="innerDiv">
                <span class="outerspan">
                    <strong class="inner">我是小白</strong>
                </span>
            </div>
        </p>
    </section>
    <script>
        document.querySelector('.inner').onclick = function (e) {
            console.log('1');
            console.log(e.currentTarget);
        }
        document.querySelector('.outerspan').onclick = function (e) {
            console.log('2');
            console.log(e.currentTarget);
        }
        document.querySelector('.innerDiv').onclick = function () {
            console.log('3');
        }
        document.querySelector('.outsetSpan').onclick = function () {
            console.log('4');
        }
        document.querySelector('.waiSpan').onclick = function () {
            console.log('5');
        }
        document.querySelector('.guSpan').onclick = function () {
            console.log('6');
        }
        document.querySelector('.jiaoSpan').onclick = function () {
            console.log('7');
        }
        document.onclick = function () {
            console.log('8');
        }
        window.onclick = function () {
            console.log('9');
        }
    </script>
</body>
</html>

十分简单的代码,方便和我一样的学生党看,点击我是小白后,结果如下:

我们发现,代码从 最里层的span=>父级span=>到父级div=>...=>body=>html=> document=> window每个事件都执行了。

为什么连不是元素的window和document都执行了呢?我们需要知道每个html元素都是一个对象,所以说对上一级来说,每个元素都是该上级的一个节点,对于html来说最高的就是document对象,我们只有通过document才能访问html页面中的所有元素,而由于文档对象document是在浏览器窗口显示的,而window对象表浏览器打开的窗口,所以说document对象外层还包裹着一个window对象,所以说元素冒泡,可以看做是DOM树对象中一个节点中的一个节点触发了事件,一级一级向上触发父节点事件的过程。

其实很多场景我们都不需要事件冒泡带来的效果,很消耗性能,目前我知道5种方法可以阻止冒泡事件带来的效果,记住,我说的是效果,没说一定阻止了它。由于所限,就知道这么多。

第一种 e.stopPropagation();阻止事件传播,我们知道触发了事件,会有一个事件参数,里面记录了事件发生的各种信息,你会发现打印的事件参数对象的里面并没有stopPropagation这个方法,无论是其原型还是构造器。我们打印e.stopPropagation我们看到:

这很好的说明了为什么没有显示stopPropagation,native code翻译为本机代码,我们要知道本机代码是程序自带的,是二进制编译显示不出来的,所以别想console.log()就能打印出来。他的作用是阻止事件冒泡,执行了过后,就像我写的html代码点击了 ‘我是小白'后,只打印了1而已。

第二种方法return false 我们知道return可以阻止作用域内的return后面的代码的执行。这个作用就是可以阻止默认事件,和阻止冒泡效果,当由于作用域的原因,最里层事件的return 不能阻值其父级元素的事件触发,想要达到阻止的效果,必须每一个事件函数区域的第一行来写个return,不建议用。

第三种方法委托事件,明知山有虎,偏向虎山行。说的就是这个东西,既然知道了冒泡事件会触发给父元素,那么就给父元素添加事件,自身不添加,你会发现,当你点击该元素的时候,父级的事件触发了,这样写的好处是节省代码,提高性能。虽说冒泡,但事件只执行了一次。

第四种方法那就是使用捕获机制,捕获机制挂钩的2种绑定事件的方式 addeventListener和attachEvent,我们可称之为DOM2事件流的绑定方式,主要讲解一下addeventlistener ,addeventListener和普通的on绑定事件区别主要有3点,一是addeventListener可以控制事件是否是捕获,on不行,二是addeventLinster可以一次执行多个相同的事件做不同的事,三是因为2,所以其既可以执行冒泡又可以执行捕获,但顺序不一样。因为w3c把事件冒泡和事件捕获做了个整合,当你点击元素的时候,他先进行捕获机制后进行冒泡机制,先是从window出发,一级一级的触发事件,直至找到点击事件的触发源,触发源接受到事件,我们称为处于目标阶段,目标接受到事件以后就可以进行冒泡事件了,一层一层的父元素被触发,直到window。这个过程叫做DOM2事件流的3个阶段。想阻止冒泡就将addevevtlistener的第三个参数改为true就行,意味着使用捕获。同时我们也知道了捕获就是从外到里,和冒泡完全相反。实例:

    document.querySelector('.inner').addEventListener('click', function (e) {
            console.log('1 捕获');
            console.log(e);
        }, true);
        document.querySelector('.innerDiv').addEventListener('click', function (e) {
            console.log('3 捕获');
            console.log(e);
        }, true);
        document.querySelector('.outerspan').addEventListener('click', function (e) {
            console.log('2 捕获');
        }, true);
        document.querySelector('.inner').addEventListener('click', function (e) {
            console.log('1 冒泡');
            console.log(e);
        },false);
        document.querySelector('.innerDiv').addEventListener('click', function (e) {
            console.log('3 冒泡');
            console.log(e);
        }, false);
        document.querySelector('.outerspan').addEventListener('click', function (e) {
            console.log('2 冒泡');
        }, false);

结果

第五种方法 事件参数对象里有个cancelBubble,如果是冒泡的话,他就是false,想要不冒泡,设置 e.cancelBubble = true就行。 关于target 和 currentTarget,简单的说一下, target就是触发事件的具体的元素,就是目标源。currentTaregt就相当于this。如果你在子元素本身有绑定事件的话,那么target == currentTarget ; 如果类似事件委托的话,点击子元素,发现currentTarget是该父元素,而target指向子元素。

其实想说的还有好多,但发现已经写了这么多,就算了,这次掘金写文章真的是我变成话痨了。。。以后我会试着精简语言,以最直白的方式和最容易理解的方式来阐述我的观点,再发现掘金是发表技术的地方,可能此次不太适合,以后尽量剖点技术来发表。