JavaScript 事件冒泡和事件捕获

news/2024/7/7 9:56:37

什么是事件

  •  JavaScript和HTML之间的交互是通过事件实现的。
  •  事件:就是文档或浏览器窗口发生的一些特定的交互瞬间。
  •  监听器(或事件处理程序):预定事件,以便事件发生时执行相应的代码。
  •  通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者)

什么是事件流

  • 事件流描述的就是从页面中接受事件的顺序。
  • 而早起的IE和Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而Natscape的事件流就是事件捕获。

事件冒泡事件捕获

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

      

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

    由于IE低版本浏览器不支持,所以很少使用事件捕获

        

后来ECMAScript在DOM2中对事件流进行了进一步规范,DOM2级事件规定的事件流包括三个阶段:

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段

DOM事件处理

DOM节点中有了事件,那我们就需要对事件进行处理,而DOM事件处理分为4个级别:DOM0级事件处理、DOM1级事件处理、DOM2级事件处理、DOM3级事件处理。

                                           Image

 

DOM0:只支持事件冒泡

 dom0级事件具有几号的夸浏览器优势,会以最快的速度绑定。

 第一种方式是内联模型(行内绑定),将函数名直接作为html标签中属性的属性值。  

<div onclick="btnClick()">click</div>
<script>
    function btnClick(){
        console.log("hello");
    }
</script>

  缺点:不符合w3c中关于内容与行为分离的基本规范

第二种方式是脚本模型(动态绑定),通过在js中选中某个节点,然后给节点添加onclick属性。

直接给onclick赋值一个function

<div id="btn">点击</div>
<script>
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        console.log("hello");
    }
    // 会覆盖上一个
    btn.onclick = function(){
        console.log("hello again");
    }
</script>

缺点:如果添加两个handler,后面一个会覆盖前面一个。(因为是属性赋值)

 

嵌套的元素事件

<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.onclick=function(){
        console.log(1)
    }
    btn2.onclick=function(){
        console.log(2)
    }
    btn3.onclick=function(){
        console.log(3)
    }
</script>

由于事件冒泡:从下往上,依次触发事件。

DOM2

进一步规范之后,有了DOM2级事件处理程序。可以为一个节点添加多个事件处理。

其中定义了两个方法:

1、addEventlistener() 添加时间侦听器

  •     添加的函数不可以重名,否则会被覆盖

2、removerEventListener()  删除事件侦听器

  •       无法删除匿名函数

函数均有3个参数:

1、要处理的事件名

2、事件处理函数

3、boolean值,默认false 表示使用冒泡机制,true表示捕获机制。

<div id="btn">点击</div>

<script>
  var btn=document.getElementById("btn");
  btn.addEventListener("click",hello,false);
  btn.addEventListener("click",helloagain,false);
  // 【第一个处理函数】
  function hello(){
    console.log("hello");
  }
  // 【第二个处理函数】
  function helloagain(){
    console.log("hello again");
  }
</script>

添加多个handler,则按照添加顺序执行。

 

那么既有冒泡,也有捕获的时候。谁先谁后呢?

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('btn2冒泡')
}, false)

// 调换所有顺序
btn1.addEventListener('click',function(){
    console.log('btn1冒泡')
}, false)
btn1.addEventListener('click',function(){
    console.log('btn1捕获')
}, true)

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('btn2冒泡')
}, false)

但是最sub的元素会按照添加的顺序来执行。父元素们的捕获和冒泡顺序照旧。

阻止后序事件

有时候我们需要点击事件不再继续传递,我们在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(e){
    console.log('btn3捕获')
}, true)

  • 所有后续事件全部不会执行,包括btn1自身
  • 如果添加在btn1,不管添加在哪一个,都会执行完所有的btn1处理。但是不会执行执行父级冒泡的内容。

1.preventDefault:阻止标签默认行为,有些标签有默认行为,例如a标签的跳转链接属性href等。

  •   form中的submi点击默认行为是提交表单,这里并不需要它提交,只需要执行register方法,故阻止为秒。

2. stopPropagation:阻止事件冒泡,点击那个元素,就只响应这个元素,父级就不会响应了

 

事件委托

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

正常情况我们给每一个li都会绑定一个事件,但是如果这时候li是动态渲染的,数据又特别大的时候,每次渲染后(有新增的情况)我们还需要重新来绑定,又繁琐又耗性能;这时候我们可以将绑定事件委托到li的父级元素,即ul。

修改前:

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

修改后

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

给ul添加事件

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

 

总结

 冒泡

        概念: 从子元素向上触发

        触发方式:

           dom0方式添加时间

                 1、内联方式: 在标签内添加onclick事件

                 2、dom元素事件属性:document.getElementById('id').onclick = btnclick

          dom2方式

                  1、通过addEventLisenter(或者+false,默认是false): btn.addEventListener("click",btnClick,false);

                  2、为啥false是冒泡?可能是因为其他的方式都会产生冒泡,因此addEventLisenter默认是为了捕获而出现的吧,但是捕获的顺序比较特殊,因此false冒泡是默认

      阻止方式

            btn1是btn的父元素,因此理论上会:子捕获 =》父冒泡

           然而,子元素阻止了冒泡:子捕获

 

btn1.addEventListener('click',function(){ //父
    console.log('btn1冒泡')
}, false)

btn.addEventListener('click',function(ev){ //子
    ev.stopPropagation();
    console.log('btn捕获')
}, true)

 

   捕获

           概念:元素从上往下触发

           触发方式

                     dom2方式

                              通过addEventLisenter+true:btn.addEventListener("click",btnClick,true);

           阻止方式

                和冒泡一样

                event.stopPropagation():阻止之后发生的所有事件,如果父元素设置了阻止冒泡,那么子元素所有的事件都不会发生。但其实父元素先发生的事件对于子元素来说属于事件捕获。

 

    事件委托

         1、 某个元素中的每个子元素都需要添加一个事件,但逐个添加太浪费了

              处理事件时,可以通过   currentTarget 操作事件绑定元素 =》子元素

         2、 直接在父元素添加事件

             子元素全部不需要添加事件

            处理事件时,用target 操作真正触发的元素 =》 子元素。因此 currentTarget获取的绑定元素是父元素

   currentTarget    和  target

  1.    target 是事件触发的真是元素
  2.   currentTarget    是事件绑定的元素

https://www.wolai.com/mary/iPeoDYW5mvo2Zazb71cGv9

https://zhuanlan.zhihu.com/p/114276880

 

 


http://www.niftyadmin.cn/n/3655769.html

相关文章

初涉龙芯I/龙芯II

最近想开发一款高性能的嵌入式多媒体系统&#xff0c;目前Arm9系列的嵌入式平台多媒体性能欠佳。最近龙芯在嵌入式方面投入了很大的精力&#xff08;毕竟做通用PC&#xff0c;龙芯的性能还是远远不够&#xff0c;目前的性能不过相当于奔腾800&#xff09;&#xff0c;听我们老总…

javaScript 的 splice和slice,map和forEach、 filter()、reduce()的区别

map和forEach 参数&#xff1a;都是&#xff08;当前值&#xff0c;当前对应的index&#xff0c;当前调用的array&#xff09; 可以不写全 map 根据每个元素的转换&#xff0c;组成新数组原数组不变返回新数组注意一个笔试题&#xff1a;参考回调函数 ["1", &qu…

.net精简框架集下的ini文件读取(C#)

无论是.net框架集还是.net精简框架集都能非常完美的支持XML文件&#xff0c;并且微软也强烈建议用xml文件取代ini文件&#xff0c;但是在工控领域大部分的工程都是采用ini文件配置的系统信息的。以前的隧道管理系统是采用ini文件配置信息的&#xff0c;为了实现兼容&#xff0c…

什么事堆?什么是栈? 它们之间有什么区别和联系

堆和栈的概念存在于数据结构中和操作系统内存中。 在数据结构中&#xff0c;栈中数据的存取方式为先进后出。 堆是一个优先队列&#xff0c;是按优先级来进行排序的&#xff0c;优先级可以按照大小来规定。完全二叉树是堆的一种实现方式。 在操作系统中&#xff0c;内存被分…

也谈正则表达式

其实很早就知道了正则表达式&#xff0c;在集成VBScript脚本的时候&#xff0c;就看到了该功能&#xff0c;不过那时觉得很难&#xff0c;觉得也派不上什么用场&#xff0c;所以也没有过多关注。最近看了孟岩老师的关于正则表达式讲解&#xff0c;有一种学习正则表达式的冲动&a…

内部属性[[class]]是什么?

所有的 typeof 返回值为 “object” 的对象&#xff08;如数组&#xff09;都包含一个内部属性 [[class]] (我们可以把它看作一个内部的分类)&#xff0c;而非传统的面向对象意义上的类&#xff09;。 这个属性无法直接被访问&#xff0c;一般通过 object。prototype。toStrin…

WinCE5.0平台下的Moxa DA66x设备应用开发心得

最近金日隧道广告系统的通信系统要升级&#xff0c;用Moxa的DA66x设备取代原先的Moxa5630通信模块&#xff0c;由于DA66x内嵌了WinCE5.0系统&#xff0c;系统的功能可以有很大的发挥余地。DA66x这款产品功能很强&#xff0c;可以说WinCE5.0在工业通信领域被用到了极致&#xff…

介绍 js 有哪些内置对象?

涉及的知识点 js 中的内置对象主要指的是在程序执行前存在全局作用域里的由 js 定义的一些全局值属性、函数和用来实例化其他对象的构造函 数对象。一般我们经常用到的如全局变量值 NaN、undefined&#xff0c;全局函数如 parseInt()、parseFloat() 用来实例化对象的构 造函数…