zoukankan      html  css  js  c++  java
  • 562 DOM0级、DOM2级事件,事件对象,阻止默认行为、冒泡,事件传播机制,事件委托

    DOM0级、DOM2级事件及其原理

    事件是元素(或者浏览器)天生自带的行为,只要行为触发,则会触发相关的事件行为;

    事件绑定:基于xxx.onxxx = function(){},属于给某个事件行为绑定方法,再行为触发的时候可以通知方法执行

    * 事件绑定

    * 1.DOM0级事件绑定

    * xxx.onxxx=function(){}

    * 2.DOM2级事件绑定

    * EventTarget.prototype:

    * addEventListener、removeEventListener、dispatchEvent

    * 所有的DOM元素对象(含window)都是EventTarget的实例

    * 非标准浏览器(IE<=8):attachEvent/detachEvent

    * xxx.addEventListener、removeEventListener('xxx',function(){}, false)

    * 【DOM0事件绑定的原理】

    * 每一个DOM元素对象都有很多内置的私有属性,其中包含onxxx这样事件类的私有属性

    * 1、DOM0事件绑定原理

    * 给这些事件类私有属性赋值(当我们触发相关事件行为,浏览器会帮助我们把赋值的函数触发执行)

    * (1)特点1:如果不存在某个事件类型的私有属性,则无法基于这种方式做事件绑定(例如 DOMContentLoaded [等到DOM资源加载完触发的事件])

    * (2)特点2:只能给当前元素的某个事件类型绑定一个方法(私有属性只能赋值一个值)

    *

    * 2、DOM2事件绑定的原理

    * 利用浏览器的事件池机制来完成事件监听和绑定的 【自动去重】

    * (1)特点1:所有事件类型都可以基于这种方式进行事件绑定( 例如 window.addEventListener('DOMContentLoaded',function(){}) )

    * (2)特点2:可以给当前元素的某一个事件类型绑定多个不同的方法

    box.onclick = function () {
        console.log('OK');
    };
    
    
    box.addEventListener('click', function () {
        console.log('DOM2=>OK');
    });
    box.addEventListener('click', function () {
        console.log('DOM2=>NO');
    }); 
    

    1594711379182


    问:window.onload和document.ready区别(JQ中的$(document).ready())

    我之前看过部分JQ源码,其中包含$(document).ready()的处理

    document.addEventListener("DOMContentLoaded", completed)

    =>1)它是基于DOM2级事件中事件池监听实现事件绑定的,所以可以在相同页面中给事件绑定好多不同的方法,也就是可以使用多次

    =>2)它监听的是DOMContentLoaded事件,等待DOM结构一加载完就会触发执行的

    而window.onload本身基于DOM0事件绑定,而且监听的是load事件,所以页面中不仅只能用一次,而且需要等到浏览器所有资源都加载完毕才会触发执行,触发的节点要晚于DOMContentLoaded

    ....

    所以我们之前做项目的时候,有的项目没有用JQ,我想在DOM结构加载完再去做一些事情,我就仿照着JQ写过一些公共方法


    事件对象,阻止默认行为、冒泡

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
      <title>哈哈</title>
      <style>
        #box {
          box-sizing: border-box;
           100px;
          height: 100px;
          background: lightcoral;
        }
      </style>
    </head>
    
    <body>
      <div id="box"></div>
    
      <script>
    
        /*
        * 事件对象
        *    【鼠标事件对象】 MouseEvent
        *        clientX、clientY: 鼠标触发点距离当前窗口左上角的X/Y轴坐标
        *        pageX、pageY: 鼠标触发点距离BODY(页面首屏)左上角的X/Y轴坐标
        *        path: 存储的是冒泡阶段需要传播的路径(值是捕获阶段获取的)
        *        srcElement、target: 事件源(当前操作的元素)
        *        type: 事件类型
    
        *    【键盘事件对象】 KeyboardEvent
        *        which/keyCode:键盘按键的键盘码
        *        shiftKey/altKy/ctrlKey: 记录是否在按键的时候,按下这些键(组合按键,属性值是布尔类型)
        
        *    【常规事件对象】 Event
        *    【手指事件对象】 TouchEvent
        *    ......
        * 
        * Event.prototype
        *     阻止事件的默认行为
        *     ev.preventDefault() / ev.returnValue=false
        *     
        *     阻止事件的冒泡传播
        *     ev.stopPropagation() / ev.cancelBubble=true
        */
    
    
        let n;
        document.body.onclick = function (ev) {
          console.log(111)
          console.log('BODY');
          console.log(n === ev); // => true
        };
    
        box.onclick = function (ev) {
          // 当事件行为触发,浏览器会把绑定的方法执行,并且把全局下记录当前操作信息的“事件对象”传递给这个函数  =>  当前做了某些操作,不管在哪一个函数中,我们获取的事件对象是同一个,存储的是当前操作的信息,和函数没关系
          console.log('BOX');
          n = ev;
          console.log(ev);
        };
    
    
        document.onkeydown = function (ev) {
          let {
            ctrlKey,
            keyCode
          } = ev;
    
          // 按CTRL+F, 禁止默认行为, 我们期望它刷新页面
          if (ctrlKey && keyCode === 70) {
            ev.preventDefault();
            location.href = location.href;
          }
        };
    
        document.oncontextmenu = function (ev) {
          // 禁止右键菜单
          ev.preventDefault();
          // return false; 这也是阻止默认行为
        }; 
      </script>
    </body>
    
    </html>
    

    事件传播机制

    * 事件具备传播机制

    * 捕获 CAPTURING_PHASE 1

    * 目标 AT_TARGET 2

    * 冒泡 BUBBLING_PHASE 3

    * 当我们触发当前元素的某个事件行为的时候:

    * 1.首先会从最外层window开始,一层层的按照结构向里层查找【捕获:为冒泡阶段提供传播的路径 => ev.path】

    * 2.找到当前的事件源,触发当前事件源的相关行为 【目标】

    * 3.不仅当前事件源的相关行为被触发,其所有祖先元素的相关事件行为都会被触发(在这个过程中,哪一个元素的事件行为绑定了方法,方法都会被触发执行,而且顺序是由里层向外层传播) 【冒泡】

    1594711400697
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
      <title>哈哈</title>
      <!-- IMPORT CSS -->
      <link rel="stylesheet" href="css/reset.min.css">
      <style>
        #outer {
          margin: 20px auto;
           300px;
          height: 300px;
          background: pink;
        }
    
        #inner {
          margin: 20px auto;
           200px;
          height: 200px;
          background: cyan;
        }
    
        #center {
          margin: 20px auto;
           100px;
          height: 100px;
          background: orchid;
        }
      </style>
    </head>
    
    <body>
      <div id="outer">
        <div id="inner">
          <div id="center"></div>
        </div>
      </div>
    
      <!-- IMPORT JS -->
      <script>
        /* 大部分事件默认都会存在冒泡传播机制,但是少部分事件天生自己就阻止了冒泡传播 */
    
        // mouseover/mouseout  
        inner.onmouseover = function () {
          console.log('INNER OVER');
        };
        inner.onmouseout = function () {
          console.log('INNER OUT');
        };
        outer.onmouseover = function () {
          console.log('OUTER OVER');
        };
        outer.onmouseout = function () {
          console.log('OUTER OUT');
        };
    
    
        // mouseenter/mouseleave
        inner.onmouseenter = function () {
          console.log('INNER ENTER');
        };
        inner.onmouseleave = function () {
          console.log('INNER LEAVE');
        };
        outer.onmouseenter = function () {
          console.log('OUTER ENTER');
        };
        outer.onmouseleave = function () {
          console.log('OUTER LEAVE');
        };
      </script>
    
    
      <script>
        window.addEventListener('click', function () {
          console.log('WINDOW');
        });
    
        document.addEventListener('click', function () {
          console.log('DOCUMENT');
        });
    
        // HTML
        document.body.addEventListener('click', function () {
          console.log('BODY');
        });
    
        outer.addEventListener('click', function () {
          console.log('OUTER');
        });
    
        inner.addEventListener('click', function () {
          console.log('INNER');
        });
    
        center.addEventListener('click', function (ev) {
          console.log('CENTER');
          // 阻止冒泡传播
          // ev.stopPropagation();
        }); 
      </script>
    </body>
    
    </html>
    

    事件委托

    • 事件委托 / 事件代理:
    • 利用事件的冒泡传播机制,我们把当前元素事件触发要做的事情,全部委托给外层容器,这样触发当前元素的某个事件行为,其外层容器(祖先元素)的相关事件行为也会被触发,再给外层容器事件触发绑定的方法中,基于不同事件源,处理要做的不同的事情。
    • 事件委托/事件代理的优势:
      1. 性能很好(比一般的事件绑定性能提高40%以上,尤其需要单独绑定的元素越多,性能越好)=> 项目中遇到一个容器中很多元素的某个行为触发要做一些事情,此时杜绝一个个的绑定,直接基于事件委托处理(Vue项目中也一样)
      1. 灵活,基于阻止冒泡传播,可以灵活控制哪些走事件代理,哪些不需要走
      1. 某些需求必须基于事件委托来实现:例如点击A、B做什么,剩下不管点击谁都统一做什么...
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
      <title>哈哈</title>
      <!-- IMPORT CSS -->
      <link rel="stylesheet" href="css/reset.min.css">
      <style>
        #outer {
          margin: 20px auto;
           300px;
          height: 300px;
          background: red;
        }
    
        #inner {
          margin: 20px auto;
           200px;
          height: 200px;
          background: green;
        }
    
        #center {
          margin: 20px auto;
           100px;
          height: 100px;
          background: blue;
        }
      </style>
    </head>
    
    <body>
      <div id="outer">
        <div id="inner">
          <div id="center"></div>
        </div>
      </div>
    
      <!-- IMPORT JS -->
      <script>
        document.body.onclick = function (ev) {
          let target = ev.target,
            id = target.id;
          if (id === "outer") {
            console.log('OUTER');
            return;
          }
          if (id === "inner") {
            console.log('INNER');
            return;
          }
          console.log('啥也不是');
        };
    
        center.onclick = function (ev) {
          console.log('CENTER');
          ev.stopPropagation();
        };
    
    
    
        // 不用事件委托的写法
        center.onclick = function (ev) {
          console.log('CENTER');
          ev.stopPropagation();
        };
        inner.onclick = function (ev) {
          console.log('INNER');
          ev.stopPropagation();
        };
        outer.onclick = function () {
          console.log('OUTER');
        }; 
      </script>
    </body>
    
    </html>
    
  • 相关阅读:
    Binary Tree Zigzag Level Order Traversal
    Binary Tree Level Order Traversal
    Symmetric Tree
    Best Time to Buy and Sell Stock II
    Best Time to Buy and Sell Stock
    Triangle
    Populating Next Right Pointers in Each Node II
    Pascal's Triangle II
    Pascal's Triangle
    Populating Next Right Pointers in Each Node
  • 原文地址:https://www.cnblogs.com/jianjie/p/13868379.html
Copyright © 2011-2022 走看看