js事件冒泡和事件捕获

事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。

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

上面的代码中一个div元素中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数首先被触发呢?
为了解决这个问题,微软和网景提出了两种完全相反的概念。

1.事件冒泡


微软提出了名为事件冒泡的事件流。这个事件流是从内而外的去触发事件,即从触发事件的那个节点一直到document。也可以理解为自下而上。
上述代码,如果在事件冒泡的情况下,触发p元素的click事件的顺序应该是:p->div->body->html->document

2.事件捕获


网景提出了另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件捕获的事件流是从最外层开始发生,直到被触发的具体元素。也可以理解为自上而下。
上述代码中,如果在事件捕获的情况下,触发p元素的click事件的顺序应该是:document->html->body->div->p

3.addEventListener的第三个参数


这个一个给事件绑定监听函数的方法,它接收三个参数

element.addEventListener(event, function, useCapture)

第一个参数是需要绑定的事件(字符串,指定事件名,不要用on前缀,使用click而不是使用onclick)
第二个参数指定事件触发时执行的函数
第三个参数默认为false,指定事件流为事件冒泡,设为为true时指定为事件捕获。

4.举例子

//事件冒泡
<div  id="s1">s1
    <div id="s2">s2</div>
</div>
<script>
    s1.addEventListener("click",function(){
        console.log("s1冒泡事件")
    });
    s2.addEventListener("click",function(){
        console.log("s2冒泡事件")
    })
</script>

//结果
点击s1输出结果:s1冒泡事件
点击s2输出结果:s2冒泡事件,s1冒泡事件
  1. 当点击s1时,先触发s1的点击事件,然后向上触发直至document,上面没有其他的点击事件了,所以只输出“s1冒泡事件”这个结果。
  2. 当点击s2时,先触发s2的点击事件,所以先打印出“s2冒泡事件”,然后向上触发至s1,又打印出“s1冒泡事件”,再向上触发直至document。
//事件捕获
<div  id="s1">s1
    <div id="s2">s2</div>
</div>
<script>
    s1.addEventListener("click",function(){
        console.log("s1捕获事件")
    },true);
    s2.addEventListener("click",function(){
        console.log("s2捕获事件")
    },true)
</script>

//结果
点击s1输出结果:s1捕获事件
点击s2输出结果:s1捕获事件,s2捕获事件
  1. 当点击s1时,先触发最外层document,然后向下触发直至具体元素s1,只有s1上监听了click事件,所以只输出“s1捕获事件”这个结果。
  2. 当点击s2时,先触发最外层document,然后向下触发s1的click事件,所以打印“s1捕获事件”这个结果,然后继续向下触发到s2,又打印出“s2捕获事件”。

5.事件冒泡VS事件捕获


当事件捕获和事件冒泡一起存在的情况下,事件又是如果触发的呢?
这里把被点击的DOM节点记为target节点。

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

总结就是:

  1. 对于非target节点,先捕获再冒泡
  2. 对于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>

//结果
点击s1,输出:s1冒泡事件,s1捕获事件
点击s2,输出:s1捕获事件,s2冒泡事件,s2捕获事件,s1冒泡事件

5.事件委托

  1. 事件委托是利用事件冒泡的原理。
  2. 使用情况:当有多个类似的元素需要绑定事件时,一个一个去绑定既浪费时间,又不利于性能,这时候可以使用事件委托,给他们的一个共同父级元素添加一个事件函数去处理所有的事件情况。
<ul id="father">
   <li>1</li>
   <li>2</li>
   <li>3</li>
   <li>4</li>
   <li>5</li>
</ul>
<script>
   father.addEventListener("click",function(e){
       if(e.target!=father){
           console.log(e.target.innerText)
       }
   });
</script>

//结果
点击哪个li,就输出那个的文字

因为采用的事件冒泡的事件流,所以当点击li元素时,向上触发他们的父元素上的点击事件。
优点:
1、性能 不需要循环所有的元素一个个绑定事件
2、灵活 当有新的子元素时不需要重新绑定事件

取消冒泡事件:
w3c标准 event.stopPropagation(),但ie9以下版本不支持

scroll,mouseleave ,mouseenterfocus,blur,focus,change,submit,reset,select等事件不冒泡。
hover事件不能使用事件委托方式。(鼠标移动到元素上)

转载于:https://segmentfault.com/a/1190000005654451

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值