事件冒泡和事件捕获
1、事件阶段
如下图所示
Paste_Image.png
一般的,事件分为三个阶段:捕获阶段、目标阶段和冒泡阶段。
(1)捕获阶段(Capture Phase)
事件的第一个阶段是捕获阶段。事件从文档的根节点流向目标对象节点。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主要任务是建立传播路径,在冒泡阶段,事件会通过这个路径回溯到文档跟节点。
(2)目标阶段(Target Phase)
当事件到达目标节点的,事件就进入了目标阶段。事件在目标节点上被触发,然后会逆向回流,直到传播至最外层的文档节点。
(3)冒泡阶段(Bubble Phase)
事件在目标元素上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,回溯到根节点。
冒泡过程非常有用。它将我们从对特定元素的事件监听中释放出来,如果没有事件冒泡,我们需要监听很多不同的元素来确保捕获到想要的事件。
2、冒泡阶段调用事件处理函数
Paste_Image.png封装一个getTag函数,当点击a标签的时候,由于是冒泡机制,会从目标节点向上逐级触发各个节点a,li,ul,div的事件处理函数。
3、捕获阶段调用事件处理函数
Paste_Image.png在function回调函数里加个true,则为事件捕获。当点击a标签的时候,由于是捕获机制,会从根节点向下逐级触发各个节点直到目标节点触发事件处理函数即div,ul,li,a
4、事件代理
在传统的事件处理中,你按照需要为每一个元素添加或者是删除事件处理器。然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越大。JavaScript事件代理可以把事件处理器添加到一个父元素上,这样就避免了把事件处理器添加到多个子元素上。
(1)它是怎么运作的呢?
事件代理用到了两个JavaSciprt事件特性:事件冒泡以及目标元素。当一个元素上的事件被触发的时候,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;使用事件代理,我们可以把事件处理器添加到一个元素上,等待一个事件从它的子级元素里冒泡上来,并且可以得知这个事件是从哪个元素开始的。
(2)这对我有什么好处呢?
比如说在一个10列、100行的HTML表格里,让其每一个单元格在被点击的时候变成可编辑状态。如果把事件处理器加到这1000个单元格会产生一个很大的性能问题,并且有可能导致内存泄露甚至是浏览器的崩溃。相反地,使用事件代理,你只需要把一个事件处理器添加到table元素上就可以了,这个函数可以把点击事件给截下来,并且判断出是哪个单元格被点击了。
(3)另一个简单的例子
Paste_Image.png如图所示,如果我们需要对每个li元素进行点击,触发事件。传统的我们要像绿色注释部分那样给每一个li元素添加事件处理函数。但是通过事件代理,我们只需要在ul元素进行事件监听就可以实现点击每一个li都能触发事件了
5、阻止事件冒泡(stopPropagation)
(1)为什么要阻止事件冒泡
有种可能是,某个DOM节点绑定了某事件监听器,本来是想当该DOM节点触发事件,才会执行回调函数。结果是该节点的某后代节点触发某事件,由于事件冒泡,该DOM节点事件也会触发,执行了回调函数,这样就违背了最初的本意了。
(2)如何阻止事件冒泡
Paste_Image.png如图所示,在第一个li标签加上event.stopPropagation()方法就能阻止事件的冒泡,这样的话在点击第一个li标签就无法触发事件处理函数了。
(3)无法在捕获阶段阻止事件冒泡
这里需要注意的是,我们无法在事件捕获阶段阻止事件冒泡!!!例如,我们在代码里加上true,如图所示,第一个li会触发事件。因为捕获是从根节点向目标节点触发,而冒泡是从目标节点向根节点触发。
Paste_Image.png