JS事件流的一些理解

昨晚和今天在复习JS事件流,那么,就来归纳总结JS事件流的一些知识点吧.首先,我们先来讲述下,当一个节点产生一个事件时,该事件会在元素结点与根节点之间按特定的顺序传播,路径通过的节点都会收到该事件,这个传播过程称为DOM事件流.

首先,我们先来解释下什么是事件:

Javascript和HTML之间的交互是通过事件实现的.事件,就是文档或浏览器窗口发生的一些特定的交互瞬间.可以使用监听器来预定事件,以便事件发生时执行相应的代码.

那么事件流又是什么呢?

事件流描述的就是从页面中接收事件的顺序,而早期的IE与Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而NetScape的事件流就是事件捕获.

那么,接下来我们就介绍下事件冒泡与事件捕获.

IE提出的事件流是事件冒泡,即从下至上,从目标触发的元素逐级向上传播,直到window对象.

而Netscape的事件流就是事件事件捕获,即从document逐级向下传播到目标元素.

而DOM2事件的事件流则包含三个阶段:1).事件捕获阶段;2).处于目标阶段;3).事件冒泡阶段

接下来,我们来讲述下DOM2中的一些内容.

DOM2中定义了两个方法:

1.addEventListener() -- 添加事件侦听器

2.removeEventListener() -- 删除事件侦听器

函数均有三个参数,第一个参数是要处理的事件名,第二个参数是作为事件处理程序的函数,第三个参数是一个布尔值,默认为false表示使用冒泡机制,true表示捕获机制.

<div id="btn">点这个</div>
<script>
var btn=document.getElementById("btn");
btn.addEventListener("click",ljy,false);
btn.addEventListener("click",ljy1,false);
function ljy(){
console.log("ljy");
}
function ljy1(){
console.log('ljy1')
}
</script>

这时候两个事件处理程序都可以成功触发,这说明了我们可以绑定多个事件处理程序,但是注意,如果使用了一模一样的监听方式的话,会发生覆盖的,即同样的事件和事件流机制下相同方法只会触发一次.

<div id="btn">点这个</div>
<script>
var btn=document.getElementById("btn");
btn.addEventListener("click",ljy,false);
btn.addEventListener("click",ljy,false);
function ljy(){
console.log("ljy");
}
</script>

这时,ljy只会执行一次.而继续,我们把div扩展到3个.

<div id="btn3">
btn3
<div id="btn2">
btn2
<div id="btn1">
btn1
</div>
</div>
</div>
<script>
let btn1 = document.getElementById('btn1');
let btn2 = document.getElementById('btn2');
let btn3 = document.getElementById("btn3");
btn1.addEventListener("click",function{
console.log(1);
},true)
btn1.addEventListener("click",function{
console.log(2)},true)
btn1.addEventListener("click",function{
console.log(3);
},true)
</script>

那么,这个执行顺序就是3->2->1了,最外层的btn最先触发,因为addEventListener最后一个参数是true,捕获阶段进行处理.

btn1.addEventListener('click',function(){
    console.log('btn1捕获')
}, true)
btn1.addEventListener('click',function(){
    console.log('btn1冒泡')
}, false)

btn2.addEventListener('click',function(){
    console.log('btn2捕获')
}, true)
btn2.addEventListener('click',function(){
    console.log('btn2冒泡')
}, false)

btn3.addEventListener('click',function(){
    console.log('btn3捕获')
}, true)
btn3.addEventListener('click',function(){
    console.log('btn3冒泡')
}, false)

那么这个执行顺序就是,btn3捕获,btn2捕获,btn1捕获,btn1冒泡,btn2冒泡,btn3冒泡

我们看到先执行捕获阶段的处理程序,我们把顺序换一下再看看允许结果.

btn1.addEventListener('click',function(){
    console.log('btn1冒泡')
}, false)
btn1.addEventListener('click',function(){
    console.log('btn1捕获')
}, true)

btn2.addEventListener('click',function(){
    console.log('btn2冒泡')
}, false)
btn2.addEventListener('click',function(){
    console.log('btn2捕获')
}, true)

btn3.addEventListener('click',function(){
    console.log('btn3冒泡')
}, false)
btn3.addEventListener('click',function(){
    console.log('btn3捕获')
}, true)

点击btn1的时候,这个执行顺序就是:

btn3捕获-->btn2捕获-->btn1冒泡-->btn1捕获-->btn2冒泡-->btn3冒泡

点击的是btn2的时候,这个执行顺序为:

btn3捕获-->btn2冒泡-->btn2捕获-->btn3冒泡

点击btn3的时候,这个执行顺序为:

btn3冒泡->btn3捕获

需要记住这个点:非目标元素捕获-目标元素代码顺序-非目标元素冒泡(最开始看的时候,还有点不太能明白)

阻止冒泡

有时候,我们需要点击事件不再继续向上冒泡,我们在btn2上加上stopPropagation函数,阻止程序冒泡.

btn1.addEventListener('click',function(){
    console.log('btn1冒泡')
}, false)
btn1.addEventListener('click',function(){
    console.log('btn1捕获')
}, true)

btn2.addEventListener('click',function(){
    console.log('btn2冒泡')
}, false)
btn2.addEventListener('click',function(ev) {
    ev.stopPropagation();
    console.log('btn2捕获')
}, true)

btn3.addEventListener('click',function(){
    console.log('btn3冒泡')
}, false)
btn3.addEventListener('click',function(){
    console.log('btn3捕获')
}, true)

btn3捕获-->btn2捕获,可以看到btn2捕获阶段进行后不再继续往下执行.

接下来,我们顺带说下事件委托

如果有多个DOM节点需要监听事件的情况下,给每个DOM绑定监听函数,会极大的影响页面的性能,因此我们通过事件委托来进行优化,事件委托利用的就是冒泡原理.

<ul>
    <li>l</li>
    <li>j</li>
    <li>y</li>
    <li>1</li>
    <li>2</li>
</ul>
<script>
    var listen= document.getElementsByTagName('li')
    for(let index=0;index<listen.length;index++){
        listen[index].addEventListener('click',function (ev){
        console.log(ev.currentTarget.innerHTML)
    })
    }
    </script>

按照上述代码,如果我们给每一个li都会绑定一个事件,但是如果这个时候li是动态渲染的,数据又特别大的时候,若是新增的时候,我们还得重新绑定,繁琐又耗能.这时我们可以将绑定事件委托到li的父级元素,ul上.

var ulee=document.getElementByTagName('ul')
ulee[0].addEventListener('click',function(ev){
console.log(ev.target.innerHTML)
})

上面的两种代码中,我们分别用了currentTarget与target,那么这两者的区别在哪里呢?

首先,target返回触发的元素,不一定是绑定事件的元素,而currentTarget返回的是绑定事件的元素

事件委托的出现,可以提高性能,减少占用的内存空间.并且即使新增加了元素也不需要杂糅化的添加.

还有个关于这方面看到的过的一道题,事件如何先捕获后冒泡呢?

在DOM标准事件模型中,是先捕获后冒泡的.但是如果要实现先冒泡后捕获的结果,对于同一个事件,监听捕获与冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获事件.

当然也有些不支持冒泡的事件:鼠标事件:mouseleave mouseenter 焦点事件:blur focus UI事件:scroll resize.

原文地址:https://www.cnblogs.com/ljylearnsmore/p/14520154.html