• 28 【事件传播】


    事件传播

    1.事件冒泡

    事件冒泡概念:
    当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
    简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
    事件冒泡是默认存在的

    鼠标经过事件:
    mouseovermouseout 会有冒泡效果
    mouseentermouseleave 没有冒泡效果(推荐)

    		<style type="text/css">
    			#box1{
    				width: 200px;
    				height: 200px;
    				background-color: yellowgreen;
    			}
    			
    			#s1{
    				background-color: yellow;
    			}
    			
    			
    		style>
    		<script type="text/javascript">
    			
    			window.onload = function(){
    				
    				/*
    				 * 事件的冒泡(Bubble)
    				 * 	- 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
    				 * 	- 在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡
    				 * 
    				 */
    				
    				//为s1绑定一个单击响应函数
    				var s1 = document.getElementById("s1");
    				s1.onclick = function(event){
    					event = event || window.event;
    					alert("我是span的单击响应函数");
    					
    					//取消冒泡
    					//可以将事件对象的cancelBubble设置为true,即可取消冒泡,这样祖先元素相同事件就不会被触发
    					event.cancelBubble = true;
    				};
    				
    				//为box1绑定一个单击响应函数
    				var box1 = document.getElementById("box1");
    				box1.onclick = function(event){
    					event = event || window.event;
    					alert("我是div的单击响应函数");
    					
                        /*因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素。若想把事件就限制在当前元素内,就需要阻止事件流动阻止事件流动需要拿到事件对象。
    					此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效*/
    					// e.stopPropagation()
    				};
    				
    				//为body绑定一个单击响应函数
    				document.body.onclick = function(){
    					alert("我是body的单击响应函数");
    				};	
    			};
    
    		script>
    	<body>
    		
    		<div id="box1">
    			我是box1
    			<span id="s1">我是spanspan>
    		div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    2.阻止冒泡

    阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。

    stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。

    function stopEvent(e) {
      e.stopPropagation();
    }
    
    el.addEventListener('click', stopEvent, false);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面代码中,click事件将不会进一步冒泡到el节点的父节点。

    <body>
      <h3>阻止冒泡h3>
      <p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。p>
      <div class="outer">
        <div class="inner">
          <div class="child">div>
        div>
      div>
      <script>
        // 获取嵌套的3个节点
        const outer = document.querySelector('.outer')
        const inner = document.querySelector('.inner')
        const child = document.querySelector('.child')
    
        // 外层的盒子
        outer.addEventListener('click', function () {
          console.log('outer...')
        })
    
        // 中间的盒子    inner.addEventListener('click', function (ev) {
          console.log('inner...')
    
          // 阻止事件冒泡
          ev.stopPropagation()
        })
    
        // 内层的盒子
        child.addEventListener('click', function (ev) {
          console.log('child...')
    
          // 借助事件对象,阻止事件向上冒泡
          ev.stopPropagation()
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    结论:事件对象中的 e.stopPropagation 方法,专门用来阻止事件冒泡。

    3.阻止默认事件

    e.preventDefault() 方法用来阻止事件产生的 “默认动作”。

    一些特殊的业务需求,需要阻止事件的 “默认动作”。

    【小案例1】

    制作一个文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果。

    DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    
    <body>
        <p>
            只能输入小写字母和数字:
            <input type="text" id="field">
        p>
        <script>
            var oField = document.getElementById('field');
    
            oField.onkeypress = function (e) {
                console.log(e.charCode);
    
                // 根据用户输入的字符的字符码(e.charCode)
                // 数字 0~9,字符码 48~57
                // 小写字母 a~z,字符码 97~122
                if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {
                    // 阻止浏览器的默认行为
                    e.preventDefault();
                }
            };
        script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    【小案例2】

    制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加 1;反之,数字减 1。

    鼠标滚轮事件是 onwheel,它的事件对象 e 提供 deltaY 属性表示鼠标滚动方向,向下滚动是返回正值,向上滚动时返回负值。

    • 没有阻止事件的 “默认动作” 时
    DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <style>
            #box {
                width: 200px;
                height: 200px;
                background-color: #333;
            }
    
            body {
                height: 2000px;
            }
        style>
    head>
    
    <body>
        <div id="box">div>
        <h1 id="info">0h1>
    
        <script>
            var oBox = document.getElementById('box');
            var oInfo = document.getElementById('info');
    
            // 全局变量就是 info 中显示的数字
            var a = 0;
    
            // 给 box 盒子添加鼠标滚轮事件监听
            oBox.onwheel = function (e) {
                if (e.deltaY > 0) {
                    a++;
                } else {
                    a--;
                }
                oInfo.innerText = a;
            }
        script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 阻止事件的 “默认动作” 后
            // 给 box 盒子添加鼠标滚轮事件监听
            oBox.onmousewheel = function (e) {
                // 阻止默认事件:就是说当用户在盒子里面滚动鼠标滚轮的时候,此时不会引发页面的滚动条的滚动
                e.preventDefault();
    
                if (e.deltaY > 0) {
                    a++;
                } else {
                    a--;
                }
                oInfo.innerText = a;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.事件委托

    4.1 批量添加事件监听

    题目:页面上有一个无序列表

      ,它内部共有 20 个
    • 元素,请批量给它们添加事件监听,实现效果:点击哪个
    • 元素,哪个
    • 元素就变红。

      DOCTYPE html>
      <html lang="en">
      
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Documenttitle>
      head>
      
      <body>
          <ul id="list">
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
              <li>列表项li>
          ul>
      
          <script>
              var oList = document.getElementById('list');
              var lis = oList.getElementsByTagName('li');
      
              // 书写循环语句,批量给元素添加监听
              for (var i = 0; i < lis.length; i++) {
                  lis[i].onclick = function () {
                      // 在这个函数中,this表示点击的这个元素,this涉及函数上下文的相关知识,我们在“面向对象”课程中介绍
                      this.style.color = 'red';
                  };
              }
          script>
      body>
      
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48

      批量添加事件监听的性能问题

      每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大。

      实际上,每个

    • 的事件处理函数都是不同的函数,这些函数本身也会占用内存。

      4.2 新增元素动态绑定事件

      题目:页面上有一个无序列表

        ,它内部没有
      • 元素,请制作一个按钮,点击这个按钮就能增加一个
      • 元素。并且要求每个增加的
      • 元素也要有点击事件监听,实现效果:点击哪个
      • 元素,哪个
      • 元素就变红。

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">按我添加新的li列表项button>
            <ul id="list">ul>
        
            <script>
                var oBtn = document.getElementById('btn');
                var oList = document.getElementById('list');
                var lis = oList.getElementsByTagName('li');
        
                // 按钮的点击事件
                oBtn.onclick = function () {
                    // 创建一个新的li列表项,孤儿节点
                    var oLi = document.createElement('li');
                    oLi.innerHTML = '我是列表项';
                    // 上树
                    oList.appendChild(oLi);
                    // 给新创建的这个li节点添加onclick事件监听
                    oLi.onclick = function () {
                        this.style.color = 'red';
                    };
                };
                // 注意:此处不能使用上一次循环的方式,因为在 var lis = oList.getElementsByTagName('li'); 的时候还没有得到任何东西,后面添加的 li 是监测不到的。
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35

        动态绑定事件的问题

        新增元素必须分别添加事件监听,不能自动获得事件监听。

        大量事件监听、大量事件处理函数都会产生大量的内存消耗。

        4.3 事件委托

        利用事件冒泡机制,将后代元素事件委托给祖先元素。

        image-20220825151320590

        由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的委托(delegation)。

        4.4 e.target和e.currentTarget属性

        事件委托通常需要结合使用 e.target 属性。

        属性属性描述
        target触发此事件的最早元素,即 “事件源元素”
        currentTarget事件处理程序附加到的元素
        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">按我创建一个新列表项button>
            <ul id="list">
                <li>列表项li>
                <li>列表项li>
                <li>列表项li>
            ul>
            <script>
                var oList = document.getElementById('list');
                var oBtn = document.getElementById('btn');
        
                oList.onclick = function (e) {
                    // e.target表示用户真正点击的那个元素
                    e.target.style.color = 'red';
                };
        
                oBtn.onclick = function () {
                    // 创建新的li元素
                    var oLi = document.createElement('li');
                    // 写内容
                    oLi.innerText = '我是新来的';
                    // 上树
                    oList.appendChild(oLi);
                };
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37

        事件发生以后,会经过捕获和冒泡两个阶段,依次通过多个 DOM 节点。因此,任意事件都有两个与事件相关的节点,一个是事件的原始触发节点(Event.target),另一个是事件当前正在通过的节点(Event.currentTarget)。前者通常是后者的后代节点。

        Event.currentTarget属性返回事件当前所在的节点,即事件当前正在通过的节点,也就是当前正在执行的监听函数所在的那个节点。随着事件的传播,这个属性的值会变。

        Event.target属性返回原始触发事件的那个节点,即事件最初发生的节点。这个属性不会随着事件的传播而改变。

        事件传播过程中,不同节点的监听函数内部的Event.targetEvent.currentTarget属性的值是不一样的。

        // HTML 代码为
        // 

        Hello World

        function hide(e) { // 不管点击 Hello 或 World,总是返回 true console.log(this === e.currentTarget); // 点击 Hello,返回 true // 点击 World,返回 false console.log(this === e.target); } document.getElementById('para').addEventListener('click', hide, false);
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12

        上面代码中,

        的子节点,点击或者点击

        ,都会导致监听函数执行。这时,e.target总是指向原始点击位置的那个节点,而e.currentTarget指向事件传播过程中正在经过的那个节点。由于监听函数只有事件经过时才会触发,所以e.currentTarget总是等同于监听函数内部的this

        4.5 使用事件委托时需要注意的事项

        (1)onmouseenteronmouseover 都表示 “鼠标进入”,它们有什么区别呢?

        答:onmouseenter 不冒泡,onmouseover 冒泡。

        • 使用事件委托时要注意:不能委托不冒泡的事件给祖先元素。

        【onmouseenter】

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">按我创建一个新列表项button>
            <ul id="list">
                <li>列表项li>
                <li>列表项li>
                <li>列表项li>
            ul>
            <script>
                var oList = document.getElementById('list');
                var oBtn = document.getElementById('btn');
        
                // onmouseenter这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了那个DOM节点
                // 就是哪个DOM节点自己触发的事件,没有冒泡过程
                // 再因为继承性,所以所有 li 会一起变色
                oList.onmouseenter = function (e) {
                    // e.target表示用户真正点击的那个元素
                    e.target.style.color = 'red';
                };
        
                // oList.onmouseover = function (e) {
                //     // e.target表示用户真正点击的那个元素,即:那个 li
                //     e.target.style.color = 'red';
                // };
        
                oBtn.onclick = function () {
                    // 创建新的li元素
                    var oLi = document.createElement('li');
                    // 写内容
                    oLi.innerText = '我是新来的';
                    // 上树
                    oList.appendChild(oLi);
                };
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45

        【onmouseover】

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">按我创建一个新列表项button>
            <ul id="list">
                <li>列表项li>
                <li>列表项li>
                <li>列表项li>
            ul>
            <script>
                var oList = document.getElementById('list');
                var oBtn = document.getElementById('btn');
        
                // onmouseenter这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了那个DOM节点
                // 就是哪个DOM节点自己触发的事件,没有冒泡过程
                // 再因为继承性,所以所有 li 会一起变色
                // oList.onmouseenter = function (e) {
                //     // e.target表示用户真正点击的那个元素
                //     e.target.style.color = 'red';
                // };
        
                oList.onmouseover = function (e) {
                    // e.target表示用户真正点击的那个元素,即:那个 li
                    e.target.style.color = 'red';
                };
        
                oBtn.onclick = function () {
                    // 创建新的li元素
                    var oLi = document.createElement('li');
                    // 写内容
                    oLi.innerText = '我是新来的';
                    // 上树
                    oList.appendChild(oLi);
                };
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45

        (2)最内层元素不能再有额外的内层元素了,比如:

        这会导致点击 li 时效果正常,但是点击 span 时,只有 span 会变色。

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">按我创建一个新列表项button>
            <ul id="list">
                <li><span>我是spanspan>列表项li>
                <li><span>我是spanspan>列表项li>
                <li><span>我是spanspan>列表项li>
                <li><span>我是spanspan>列表项li>
                <li><span>我是spanspan>列表项li>
            ul>
            <script>
                var oList = document.getElementById('list');
                var oBtn = document.getElementById('btn');
        
                oList.onmouseover = function (e) {
                    // e.target表示用户真正点击的那个元素,即:那个 li
                    e.target.style.color = 'red';
                };
        
                oBtn.onclick = function () {
                    // 创建新的li元素
                    var oLi = document.createElement('li');
                    // 写内容
                    oLi.innerText = '我是新来的';
                    // 上树
                    oList.appendChild(oLi);
                };
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39

        5.事件流

        5.1 小案例引入

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
            <style>
                #box1 {
                    width: 202px;
                    height: 202px;
                    border: 1px solid #000;
                    padding: 50px;
                }
        
                #box2 {
                    width: 100px;
                    height: 100px;
                    border: 1px solid #000;
                    padding: 50px;
                }
        
                #box3 {
                    width: 100px;
                    height: 100px;
                    border: 1px solid #000;
                }
            style>
        head>
        
        <body>
            <div id="box1">
                <div id="box2">
                    <div id="box3">div>
                div>
            div>
            <script>
                var oBox1 = document.getElementById('box1');
                var oBox2 = document.getElementById('box2');
                var oBox3 = document.getElementById('box3');
        
                oBox2.onclick = function () {
                    console.log('我是 box2 的 onclick');
                };
        
                oBox3.onclick = function () {
                    console.log('我是 box3 的 onclick');
                };
        
                oBox1.onclick = function () {
                    console.log('我是 box1 的 onclick');
                };
            script>
        body>
        
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56

        5.2 捕获阶段和冒泡阶段

        image-20220825152855525

        一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。

        • 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
        • 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
        • 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。

        这种三阶段的传播模型,使得同一个事件会在多个节点上触发。

        5.3 addEventListener()的第三个参数

        onxxx写法只能监听冒泡阶段

        addEventListener()方法

        oBox.addEventListener('click', function(){}, true);
        
        • 1

        Event:事件

        <div>
          <p>点击p>
        div>
        
        • 1
        • 2
        • 3

        上面代码中,

        节点之中有一个

        节点。

        如果对这两个节点,都设置click事件的监听函数(每个节点的捕获阶段和冒泡阶段,各设置一个监听函数),共计设置四个监听函数。然后,对

        点击,click事件会触发四次。

        var phases = {
          1: 'capture',
          2: 'target',
          3: 'bubble'
        };
        
        var div = document.querySelector('div');
        var p = document.querySelector('p');
        
        div.addEventListener('click', callback, true);
        p.addEventListener('click', callback, true);
        div.addEventListener('click', callback, false);
        p.addEventListener('click', callback, false);
        
        function callback(event) {
          var tag = event.currentTarget.tagName;
          var phase = phases[event.eventPhase];
          console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
        }
        
        // 点击以后的结果
        // Tag: 'DIV'. EventPhase: 'capture'
        // Tag: 'P'. EventPhase: 'target'
        // Tag: 'P'. EventPhase: 'target'
        // Tag: 'DIV'. EventPhase: 'bubble'
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25

        上面代码表示,click事件被触发了四次:

        节点的捕获阶段和冒泡阶段各1次,

        节点的目标阶段触发了2次。

        1. 捕获阶段:事件从

          传播时,触发

          click事件;
        2. 目标阶段:事件从
          到达

          时,触发

          click事件;

        3. 冒泡阶段:事件从

          传回

          时,再次触发
          click事件。

        其中,

        节点有两个监听函数(addEventListener方法第三个参数的不同,会导致绑定两个监听函数),因此它们都会因为click事件触发一次。所以,

        会在target阶段有两次输出。

        注意,浏览器总是假定click事件的目标节点,就是点击位置嵌套最深的那个节点(本例是

        节点里面的

        节点)。所以,

        节点的捕获阶段和冒泡阶段,都会显示为target阶段。

        事件传播的最上层对象是window,接着依次是documenthtmldocument.documentElement)和bodydocument.body)。也就是说,上例的事件传播顺序,在捕获阶段依次为windowdocumenthtmlbodydivp,在冒泡阶段依次为pdivbodyhtmldocumentwindow

        结论:

        1. addEventListener 第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发
        2. addEventListener 第3个参数为 true 表示捕获阶段触发,false 表示冒泡阶段触发,默认值为 false
        3. 事件流只会在父子元素具有相同事件类型时才会产生影响
        4. 绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)

        5.4 removeEventListener()方法

        当我们 addEventListener() 后,该监听事件就会一直生效,直到关闭页面或是移除该对应的监听!

        removeEventListener() 方法用来移除监听事件(只能移除具名函数的监听,且方法名称后面不能带 ()

        var body = document.querySelector('body'),
        var clickTarget = document.getElementById('click-target'),
        var mouseOverTarget = document.getElementById('mouse-over-target'),
        var toggle = false;
        
        // 具名函数
        function makeBackgroundYellow() {
            'use strict';
        
            if (toggle) {
                body.style.backgroundColor = 'white';
            } else {
                body.style.backgroundColor = 'yellow';
            }
        
            toggle = !toggle;
        }
        
        // 注册监听
        clickTarget.addEventListener('click',
            makeBackgroundYellow,
            false
        );
        
        // 注册监听
        mouseOverTarget.addEventListener('mouseover', function () {
            'use strict';
        
            // 移除监听
            clickTarget.removeEventListener('click',
                makeBackgroundYellow,
                false
            );
        });
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34

        5.5 特别注意

        addEventListener() 一但注册某个事件,那么这个事件是会一直生效的,就算是该注册事件写在某个函数中,那个函数调用已经结束了,但是该事件还是会存在!因为事件的注册是直接绑定到相应的元素上的,并且是异步的,除非页面被关闭,或者是移除该监听!

        DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Documenttitle>
        head>
        
        <body>
            <button id="btn">点击button>
            <script>
                var btn = document.getElementById('btn');
        
                function demo() {
                    console.log('btn');
                }
        
                function test() {
                    btn.addEventListener('click', demo, false);
                    return 'over';
                }
                
                console.log(test());
            script>
        body>
        
        html>
        
        执行结果:
        over
        (点击按钮)
        btn
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
      • 相关阅读:
        猿创征文|信息抽取(1)——pytorch实现BiLSTM-CRF模型进行实体抽取
        代码发布方式
        Docker基础-3.本地镜像发布与容器数据卷
        征文:中秋,你愿意和她一起打地(月)鼠(饼)吗?
        startActivityForResult废弃了,用Activity Result API吧
        深度学习入门(十四)数值稳定性和模型初始化
        【python入门专项练习】-N03.列表
        赠书福利开始啦—〖Effective软件测试〗
        大学生川菜网页制作教程 学生HTML静态美食菜品网页设计作业成品 简单网页制作代码 学生美食网页作品免费设计
        基于三平面映射的地形纹理化【Triplanar Mapping】
      • 原文地址:https://blog.csdn.net/DSelegent/article/details/126526388