WebAPI(二)-事件高级

注册事件(绑定事件)

  1. 注册事件概述
    1. 给元素添加事件,称为注册事件或者绑定事件。
    2. 注册事件有两种方式:传统方式和方法监听注册方式
      1. 传统注册方式
        1. 利用 on 开头的事件 onclick

           <button onclick=“alert('hi~')”></button>
           btn.onclick = function() {}
          
        2. 特点: 注册事件的唯一性
        3. 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
      2. 方法监听注册方式
        1. w3c 标准 推荐方式
        2. addEventListener()它是一个方法
        3. IE9 之前的 IE 不支持此方法,可使用 attachEvent()代替
        4. 特点:同一个元素同一个事件可以注册多个监听器
        5. 按注册顺序依次执行
  2. addEventListener 事件监听方式

     eventTarget.addEventListener(type, listener[, useCapture])
    
    1. eventTarget.addEventListener()方法将指定的监听器注册到 eventTarget(目标对象)上,当该对 象触发指定的事件时,就会执行事件处理函数。
    2. 该方法接收三个参数:
      1. type:事件类型字符串,比如 click 、mouseover ,注意这里不要带on
      2. listener:事件处理函数,事件发生时,会调用该监听函数
      3. useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,我们再进一步学习
  3. attachEvent 事件监听方式

     eventTarget.attachEvent(eventNameWithOn, callback)
    
    1. eventTarget.attachEvent()方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
    2. 该方法接收两个参数:
      1. eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带on
      2. callback: 事件处理函数,当目标触发事件时回调函数被调用
    3. 注意:
      1. 这个方法是IE独有的,只有IE支持
      2. 这个方法只支持IE9之前,不包含IE9.IE9之后不再支持
  4. 注册事件兼容性解决方案
    1. 兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器
     function addEventListener(element, eventName, fn) {
         // 判断当前浏览器是否支持 addEventListener 方法 
         if (element.addEventListener) {
             element.addEventListener(eventName, fn); // 第三个参数 默认是false 
         } else if (element.attachEvent) {
             element.attachEvent('on' + eventName, fn); 
         } else {
             // 相当于 element.onclick = fn;
             element['on' + eventName] = fn;
         }
     }
    

删除事件(解绑事件)

  1. 删除事件的方式
    1. 传统注册方式

       eventTarget.onclick = null;
      
    2. 方法监听注册方式

       eventTarget.removeEventListener(type, listener[, useCapture]);
       eventTarget.detachEvent(eventNameWithOn, callback);
      
  2. 删除事件兼容性解决方案

     function addEventListener(element, eventName, fn) {
         // 判断当前浏览器是否支持 addEventListener 方法 
         if (element.removeEventListener) {
             element.removeEventListener(eventName, fn); // 第三个参数 默认是false 
         } else if (element.detachEvent) {
             element.detachEvent('on' + eventName, fn); 
         } else {
             // 相当于 element.onclick = null;
             element['on' + eventName] = null;
         }
     }
    

代码举例

<body>
    <button class="btn1">监听事件</button>
    <button class="btn2">attach监听</button>
    <button class="btn3">事件删除</button>
    <script>
        // 1. addEventListener事件监听
        var btn = document.querySelector('.btn1');
        // 事件类型是字符串,不带on
        btn.addEventListener('click',function(){
            alert(22);
        });

        // 同一个对象可以添加多个事件
        btn.addEventListener('click',function(){
            alert(33);
        });
        // 2. attachEvent,只支持IE浏览器,且IE<9
        var btn2 = document.querySelector('.btn2');
        // btn2.attachEvent('onclick',function(){
        //     alert('attachEvent');
        // });

        // 3. 事件删除
        var btn3 = document.querySelector('.btn3');
        btn3.addEventListener('click',fn);
        function fn() {
            alert(666);
            // 事件删除
            // btn3.removeEventListener('click',fn);
            this.removeEventListener('click',fn);
        }
    </script>
</body>

DOM事件流

  1. 事件流描述的是从页面中接收事件的顺序。
  2. 事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流
  3. 比如我们给一个div 注册了点击事件:
  4. DOM 事件流分为3个阶段:
    1. 捕获阶段
    2. 当前目标阶段
    3. 冒泡阶段
  5. 事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
  6. 事件捕获: 网景最早提出,由 DOM最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
  7. 我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具 体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点( 最具体元素)之后漂浮到水面上,这个过 程相当于事件冒泡。 pic
  8. 注意:
    1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
    2. onclick 和 attachEvent 只能得到冒泡阶段。
    3. addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕 获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理 程序。
    4. 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
    5. 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
    6. 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件,我们后面讲解。
  9. 代码举例:

     .father {
         width: 100px;
         height: 100px;
         background-color: #f00;
         padding-top: 30px;
     }
     .son {
         width: 50px;
         height: 50px;
         background-color: #0f0;
         margin: 0 auto;
     }
        
     <div class="father">
         <div class="son">son盒子</div>
     </div>
     <script>
        
         /*1. 监听事件的捕获阶段,document->html->body->father->son*/
         // addEventListener第三个参数为true
         // var son = document.querySelector('.son');
         // true,监听捕获阶段
         // son.addEventListener('click',function(){
         //     alert('son');
         // },true);
         // var father = document.querySelector('.father');
         // true,监听捕获阶段
         // father.addEventListener('click',function(){
         //     alert('father');
         // },true);
         // 效果,点击son之后,father先执行函数,之后son再执行函数
    
         /*2. 监听事件的冒泡阶段,son->father->body->html->document*/
         // addEventListener第三个参数为false或者省略
         var son = document.querySelector('.son');
         // true,监听捕获阶段
         son.addEventListener('click',function(){
             alert('son');
         });
         var father = document.querySelector('.father');
         // true,监听捕获阶段
         father.addEventListener('click',function(){
             alert('father');
         });
         // 效果,点击son之后,son先执行函数,之后father再执行函数
     </script>
    

事件对象

  1. 什么是事件对象

     eventTarget.onclick = function(event) {}
     eventTarget.addEventListener('click',  function(event) {}) 
     // 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
    
    1. 官方解释:event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。 简单理解:事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象 event,它有很多属性和方法。
    2. 比如:
      1. 谁绑定了这个事件。
      2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
      3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
  2. 事件对象的使用语法

     eventTarget.onclick = function(event) { 
         // 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
     } 
     eventTarget.addEventListener('click', function(event) { 
         // 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
     })
    
    1. 这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
    2. 当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
  3. 事件对象的兼容性方案
    1. 事件对象本身的获取存在兼容问题:
    2. 标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。
    3. 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。
    4. 解决:e = e || window.event;
  4. 事件对象的常见属性和方法
    1. e.target 和 this 的区别:
      1. this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素)
      2. e.target 是事件触发的元素。
    2. 事件对象的常见属性和方法

       e.target            /返回触发事件的对象 标准
       e.srcElement        //返回触发事件的对象 非标准,IE6-8使用
       e.type              //返回事件的类型,比如click、mouseover不带on
       e.cancelBubble      //该属性阻止冒泡 非标准,IE6-8使用
       e.returnValue       //该属性阻止默认事件(默认行为)非标准IE6-8使用,比如不让连接跳转
       e.preventDefault()   //该方法阻止默认事件(默认行为)标准,比如不让连接跳转
       e.stopPropagation()  //阻止冒泡 标准
      
  5. 代码举例:

     <ul>
         <li>234</li>
         <li>234</li>
         <li>234</li>
     </ul>
     <script>
         var ul = document.querySelector('ul');
         ul.onclick = function(e) {
             // 返回ul元素,事件的绑定者=函数的调用者
             console.log(this);
             // 返回li元素,事件的触发者,点击li才触发,不是ul触发
             console.log(e.target);
         }
     </script>
    
  6. 举例2

     <a href="http://www.baidu.com">百度</a>
     <script>
         var a = document.querySelector('a');
         // 阻止元素的默认行为,阻止a标签默认的点击跳转
         // a.addEventListener('click',function(e){
         //     // 阻止点击a标签跳转,dom标准写法
         //     e.preventDefault();
         // });
         // 传统写法
         a.onclick = function(e){
             // IE>8
             // e.preventDefault();
             // 低版本浏览器IE6-8
             // e.returnValue;
             // 可以return false 也能阻止默认的元素行为,没有兼容性问题
             return false;
         }
     </script>
    

阻止事件冒泡

  1. 阻止事件冒泡的两种方式
    1. 事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点。
    2. 事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。
    3. 阻止事件冒泡
      1. 标准写法:利用事件对象里面的 stopPropagation()方法

         e.stopPropagation()
        
      2. 非标准写法:IE 6-8 利用事件对象 cancelBubble 属性

         e.cancelBubble = true;
        
  2. 阻止事件冒泡的兼容性解决方案

     if(e && e.stopPropagation){ 
         e.stopPropagation(); 
     }else{
         window.event.cancelBubble = true; 
     }
    
  3. 代码举例

     var son = document.querySelector('.son');
     son.addEventListener('click',function(e){
         alert('son');
         // 阻止冒泡
         e.stopPropagation();
     });
     var father = document.querySelector('.father');
     father.addEventListener('click',function(){
         // 不在执行
         alert('father');
     });
    

事件委托(代理、委派)

  1. 事件委托
    1. 事件委托也称为事件代理, 在 jQuery 里面称为事件委派。
  2. 事件委托的原理
    1. 不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
  3. 事件委托的作用
    1. 我们只操作了一次 DOM ,提高了程序的性能。
  4. 案例

     <ul>
         <li>1234</li>
         <li>1234</li>
         <li>1234</li>
         <li>1234</li>
         <li>1234</li>
     </ul>
    
    1. 点击每个 li 都会弹出对话框,以前需要给每个 li 注册事件,是非常辛苦的,而且访问 DOM 的次数越多,这就 会延长整个页面的交互就绪时间。
    2. 解决办法:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器

       var ul = document.querySelector('ul');
       ul.addEventListener('click',function(e){
           // e.target: 具体点击的对象
           // 点击li,然后事件冒泡到父类ul上
           e.target.style.backgroundColor = 'pink';
       })
      

常用的鼠标事件

  1. 之前基础事件中学了一大部分,下面再补充2个

     onclick         鼠标点击左键触发
     onmouseover     鼠标经过触发
     onmouseout      鼠标离开触发
     onfous          获取得鼠标焦点触发
     onblur          鼠标失去焦点触发
     onmousemove     鼠标移动触发
     onmouseup       鼠标弹起触发
     onmousedown     鼠标按下触发
     //补充
     oncontextmenu   鼠标左键选中网页中的文字然后右键出现菜单
     onselectstart   鼠标左键滑动选中网页中的文字
    
  2. oncontextmenu、onselectstart常用方法
    1. 禁止鼠标右键菜单: contextmenu
      1. 主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

         document.addEventListener('contextmenu', function(e) { 
             e.preventDefault(); 
         })
        
    2. 禁止鼠标选中(selectstart 开始选中)

       document.addEventListener('selectstart', function(e) { 
           e.preventDefault(); 
       })
      
  3. 鼠标事件对象
    1. event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象 MouseEvent 和键盘事件对象 KeyboardEvent。

       e.clientX       返回鼠标相对于浏览器窗口可视区的x坐标
       e.clientY       返回鼠标相对于浏览器窗口可视区的y坐标
       e.pageX         返回鼠标相对于文档页面的x坐标 >=IE9
       e.pageY         返回鼠标相对于文档页面的Y坐标 >=IE9
       e.screenX       返回鼠标相对于电脑屏幕的x坐标
       e.screenY       返回鼠标相对于电脑屏幕的y坐标
      
      1. 可视区:显示网页的窗口
      2. 文档页面:文档是整个网页,文档页面是可以大于可视区的,此时滚动显示
      3. e.pageX、e.pageY最常用
  4. 代码举例:
    1. 例1;

       <!-- 禁止选中、禁止右键 -->
       点击,弹出登录框
       <script>
           // 禁止鼠标右击事件,鼠标选中上面文字后,无法右键选择
           document.addEventListener('contextmenu',function(e){
               e.preventDefault(); 
           });
           // 鼠标直接无法选择内容
           document.addEventListener('selectstart',function(e){
               e.preventDefault(); 
           });
       </script>
      

      pic

    2. 例2:让一个图标跟谁鼠标一直移动

      1. 鼠标不断的移动,使用鼠标移动事件: mousemove
      2. 在页面中移动,给document注册事件
      3. 图片要移动距离,而且不占位置,我们使用绝对定位即可
      4. 核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的 top和left 值就可以移动图片
       <style>
           //图片要移动距离,而且不占位置,我们使用绝对定位即可
           img {
               position: absolute;
           }
       </style>
          
       var pic = document.querySelector('img'); 
       document.addEventListener('mousemove', function(e) { 
           var x = e.pageX; 
           var y = e.pageY; 
           pic.style.top = y - 40 + 'px'; 
           pic.style.left = x - 50 + 'px'; 
       })
      

常用的键盘事件

  1. 常用键盘事件
    1. 事件除了使用鼠标触发,还可以使用键盘触发, 注意给文档 document 添加键盘事件

       onkeyup     某个键盘按键被松开时触发
       onkeydown   某个键盘按键被按下时触发
       onkeypress  某个键盘按键被按下时触发,但是它不识别功能键
      
      1. 注意: onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等。
      2. 三个事件执行的顺序是keydown->keypress->keyup
  2. 键盘事件对象

     keyCode     返回该键的ASCII值
    
    1. 注意: onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
    2. 每个键盘都对应的ASCII表中的值,比如A:65,a:97
  3. 注意:
    1. 绑定键盘事件的对象不一定是document,也可以是某个元素添加,就意味着在某个元素内操作键盘会响应事件
    2. 举例:给input元素添加键盘事件,可以监听键盘输入内容

       <input type="text">
       <script>
           var input = document.querySelector('input');
           // 监听input的键盘输入
           input.onkeyup = function (){
               // 获取输入内容
               console.log(this.value);
           }
       </script>
      
    3. 注意:keydown、keypress在文本框里面的特点,他们两个事件触发的时候,文字还没有落入到文本框中。
    4. keyup是文字落入到文本框内,才开始触发。
  4. 代码举例

     // document.onkeyup = function() {
     //     console.log('按键被松开');
     // }
     // document.addEventListener('keydown',function(){
     //     console.log('按键被按下');
     // })
     document.addEventListener('keypress',function(e){
         console.log('按键被按下');
         // 按下a键盘,输出97
         console.log(e.keyCode);
     })
    
  5. 案例2:点击s键,聚焦搜索框

     <!-- 点击s键,聚焦搜索框 -->
     <input type="text">
     <script>
         var input = document.querySelector('input');
         // 键盘松开再聚焦,否则其他方式聚焦后会在input输入内容
         document.onkeyup = function() {
             input.focus();
         }
     </script>
    
Table of Contents