JS 基础篇(六):事件冒泡与捕获

目录:

一、事件冒泡

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

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

在事件冒泡的概念下在p元素上发生click事件的顺序应该是p -> div -> body -> html -> document。
即如果p元素上绑定了click的处理事件将先被触发否则将冒泡到div,在div上如果绑定了click的处理事件将被触发否则将冒泡到body,以此类推,判断是否有click事件的处理函数,有就触发,没有就冒泡到上一级直到到达document级。

示例:

<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:
//s2 冒泡事件
//s1 冒泡事件

//点击s1:
//s1 冒泡事件
二、事件捕获

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

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

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

即如果document元素上绑定了click的处理事件将先被触发否则将进入到html中,在html上如果绑定了click的处理事件将被触发否则将进入到body中,以此类推,判断是否有click事件的处理函数,有就触发,没有就进入到下一级直到到达到触发元素那一级。

示例:

<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:
//s1 捕获事件
//s2 捕获事件

//点击s1:
//s1 捕获事件
三、addEventListener的第三个参数

在上面的案例中,我们对addEventListener的第三个参数进行了设置,下面将介绍下addEventListener函数第三个参数的含义。addEventListener的第三个参数就是为冒泡和捕获准备的.

addEventListener有三个参数:

element.addEventListener(event, function, useCapture)
  • 第一个参数是需要绑定的事件
  • 第二个参数是触发事件后要执行的函数
  • 第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
四、事件冒泡与事件捕获同时存在

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

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

1、document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行
2、到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则捕获事件和冒泡事件的注册顺序,先注册先执行)
3、target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发

示例:

//对于非target节点则先执行捕获在执行冒泡
//对于target节点则是先执行先注册的事件,无论冒泡还是捕获
<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);
        
s1.addEventListener("click",function(e){
        console.log("s1 捕获事件");
},true);
        
s2.addEventListener("click",function(e){
        console.log("s2 捕获事件");
},true);
</script>

点击s2,执行结果如下:
s1 捕获事件
s2 冒泡事件
s2 捕获事件
s1 冒泡事件

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

五、如何阻止事件冒泡或者事件捕获

以冒泡事件为例:

1、通过event.stopPropagation()终止事件传播
对某一个节点而言,如果不想它现在处理的事件继续往上冒泡的话,我们可以终止冒泡,即在相应的处理函数内,加入event.stopPropagation(),终止事件的广播分发,这样事件停留在本节点,不会再往外传播了。

<div id="s1">s1
    <div id="s2">s2</div>
</div>

s1.addEventListener("click",function(e){
    console.log("s1 冒泡事件");
},false);
s2.addEventListener("click",function(e){
    e.stopPropagation(); //阻止冒泡到上面元素,冒泡链在此处中断
    console.log("s2 冒泡事件");
},false);

//原先点击s2:
//s2 冒泡事件
//s1 冒泡事件

//阻止冒泡后点击s2:
//s2 冒泡事件

2、对触发事件元素进行过滤

事件包含最初触发事件的节点引用 和 绑定处理事件节点的引用,那如果节点只处理自己触发的事件即可,不是自己产生的事件不处理。
event.target指向的是触发事件的dom节点,而event.currrentTarget 指向的是绑定事件的dom节点,我们可以通过这两个target 是否相等,让绑定事件的dom节点只处理其自身触发的事件而不是下级冒泡传递上来的。

s1.addEventListener("click",function(event){
    if(event.target === event.currentTarget){
        console.log("s1 冒泡事件");
    }
},false);
s2.addEventListener("click",function(event){
    if(event.target === event.currentTarget){
        console.log("s2 冒泡事件");
    }
},false);

我们还可以获取target的节点名、id等属性进行过滤设置处理事件。

<ul id="color-list">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</ul>

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);
    }
}

元素收到事件后,判断事件是否符合要求,然后做相应的处理,然后事件继续冒泡往上传递;
通过以上方式,我们把本来每个元素都要有的处理函数,都交给了其祖父节点元素来完成了,也就是说,li将自己的响应逻辑委托给ul,让它来完成相应逻辑,自己不实现相应逻辑,这个模式,就是所谓的事件委托。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值