4

CONTENTS

  • DOM事件流
  • 事件冒泡
  • 阻止冒泡
  • 事件捕获
  • 事件委托

DOM事件流

1.什么是事件流?

事件流所描述的是从页面中接受事件的顺序

2.DOM事件流的三个阶段?

事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段

3.DOM事件流三个阶段的执行顺序?

首先发生的事件捕获,为截取事件提供机会,然后是目标接受事件,最后是事件冒泡阶段,所以可以在最后一个阶段对事件作出响应。见下图更直观:

图片描述

在dom事件流中,事件的目标在捕获阶段不会接受到事件,这意味着在捕获阶段,事件从 document  到  div 后就停止了。下一个阶段是目标阶段,于是事件在 div 上发生,并在事件处理中被看成是冒泡阶段的一部分, 然后,冒泡阶段发生,事件又传回document。

事件冒泡

1.什么是事件冒泡?
当事件发生后,这个事件就要开始传播(从里向外或者从外向里)

2.为什么要传播?

        因为事件源本身(可能)并没有处理事件的能力,即处理事件的函数(方法)并未绑定在该事件源上。例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身可能不能处理这个事件,事件必须从这个按钮传播出去,从而到达能够处理这个事件的代码中(例如我们给按钮的onclick属性赋一个函数的名字,就是让这个函数去处理该按钮的click事件),或者按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。

小案例代码如下:

​
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <div>
        <button>点击</button>
    </div>
</body>
</html>

​

当我们点击按钮button时,事件时这样传播的:

(1) button

(2) div

(3) body

(4) html

(5) document

阻止冒泡

1.为什么要阻止冒泡?

例如:document上有A事件,div有B事件,div里面的span有C事件,如果不给span和div加阻止事件冒泡的话,点击span时就会触发到div的B事件、document的A事件,当点击span时不想触发div和document的事件就要加上阻止事件冒泡,div也是一样的道理,如果我们不想让点击某个事件时父级受到影响,这时就应阻止冒泡。

eg:不加阻止冒泡事件,代码如下:

css代码:

​
   <style type="text/css">
        .box1{width:200px;height:200px;background:pink;}
        .box2{width:100px;height:100px;background:gray;}
    </style>

​

js+html代码:

​
<body>
    <div class="box1">
        <div class="box2"></div>
    </div>
    <script type="text/javascript">
        //获取对象
        var box1 = document.getElementsByClassName('box1')[0];
        var box2 = document.getElementsByClassName('box2')[0];
        //添加事件
        box1.onclick = function(){
            console.log('您点击了box1');
        }
        box2.onclick = function(){
            console.log('您点击了box2');
        }
    </script>
</body>

​

效果如下:
图片描述

如图可以看出当点击  box1  时,只会提示‘您点击了box1’  而点击   box2  时,竟然输出了两句提示,如果我们不想要这种效果,我们只想要在点击了哪个按钮后就执行该按钮的命令,也就是说阻止冒泡。

下面给出上述小案例的阻止冒泡方法:一句代码搞定,改变 box2  的触发事件,代码如下:

​
box2.onclick = function(e){
            console.log('您点击了box2');
            e.stopPropagation();
        }

效果图如下:这样就实现阻止冒泡

图片描述

2.阻止冒泡的方法。

①event.stopPropagation()方法 (这个方法小编在上面已经给出了例子,这里就不在给出具体的例子)

这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件仍然会执行,当你调用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,

②event.preventDefault()方法

这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

下面给大家一个小例子:看图说话,在下面的空白处,无论我怎么点击右键,无论在什么位置点击,都会出现默认的东西,如果我们不想要这种默认的东西怎么办呢?继续看图下面的案例,马上带你飞,走起-------

图片描述

eg:阻止浏览器右键默认事件

css代码:

<style type="text/css">
        *{margin:0;padding:0;}
        ul{list-style:none;}
        .box{position:relative;width:80px;border:1px solid gray;display: none;}
        .box  ul li{height:40px;line-height:40px;text-align:center;}
        .box  ul li:hover{background:#ccc;}
    </style>

 html+js代码:

<body>
  <div class='box'>
      <ul>
          <li>刷新</li>
          <li>删除</li>
          <li>命名</li>
      </ul>
  </div>
  <script type="text/javascript">
      //获取box对象
      var  box = document.getElementsByClassName('box')[0];
      //右键鼠标事件
      window.oncontextmenu = function(event){
        //阻止默认事件
        event.preventDefault();
        //获取鼠标点击某个位置的水平位置  X  和垂直位置   Y
        var  x=event.clientX;
        var  y=event.clientY;
        //改变 box 距离上面和左边的位置
        box.style.top = y + 'px';
        box.style.left = x + 'px';
        box.style.display = 'block';
      }
        window.onclick = function() {
            box.style.display = "none";
        }
  </script>
</body>

效果如下:
图片描述

③ return false(这里的例子就不赘述了,有心的小伙伴可以动手试试)

这个方法比较暴力,他会同时阻止事件冒泡也会阻止默认事件;写上此代码,连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation()和event.preventDefault()

事件捕获

事件捕获和事件冒泡是刚好相反的,事件捕获是指不太具体的节点应该更早的接收到事件,而最具体的节点应该最后接收到事件

案例走起:

css代码:

​
<style type="text/css">
        .box1{width:300px;height:300px;background:pink;}
        .box2{width:200px;height:200px;background:skyblue;}
        .box3{width:100px;height:100px;background:gray;}
    </style>

​

html+js代码:

​
<body>
    <div class="box1">
        <div class="box2">
            <div class="div box3"></div>
        </div>
    </div>
    <script type="text/javascript">
        //获取对象
        var box1 = document.getElementsByClassName('box1')[0];
        var box2 = document.getElementsByClassName('box2')[0];
        var box3 = document.getElementsByClassName('box3')[0];
        //点击事件
        box1.addEventListener('click',function(){
            console.log("捕获 box1");
        },true);
        box2.addEventListener('click',function(){
            console.log("捕获 box2");
        },true);
        box3.addEventListener('click',function(){
            console.log("捕获 box3");
        },true);

    </script>
</body>

​

动态效果如下:
图片描述

当我点击最里面的 box3 时,我们可以看到最外层的事件先被触发,最后才是我们点击的 box3  事件被触发,这便是事件捕获。 

事件委托

1.什么是事件委托?
事件委托又名事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
官方语言太抽象,其实就是一个生活中的原理--------班级收作业
收作业有两种方法:一是谁写完了自己去交个老师。二是写完了统一交给班长,让班长交给老师,现实当中,我们大都采用第二种方法。这就可以理解为委托,无论是谁交给老师,目的只有一个就是作业能够正常上交。
2.为什么要用委托?

一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?

在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

3.事件委托原理

事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。

4.事件委托怎么实现?

小案例继续走:

代码如下:

<body>
   <div id="box">
        <input type="button" id="add" value="添加" />
        <input type="button" id="remove" value="删除" />
        <input type="button" id="move" value="移动" />
        <input type="button" id="select" value="选择" />
    </div>
    <script type="text/javascript">
    window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                /*
                    target表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是大写,有需要的话用toLocaleLowerCase()方法就行转化

                 */
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName == 'INPUT'){
                    //获取当前的操作id
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('删除');
                            break;
                        case 'move' :
                            alert('移动');
                            break;
                        case 'select' :
                            alert('选择');
                            break;
                    }
                }
            }

        }
    </script>
</body>

效果见下图:
图片描述

当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。

5.委托总结

那什么样的事件可以用事件委托,什么样的事件不可以用呢?

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

好了,今天就到这里,下次我想介绍一下事件绑定,欢迎大家关注和阅读,以上纯属个人见解,如有不对的地方,万望指正,不胜感谢!


小菜鸟
280 声望13 粉丝

运动让我更有精力!


下一篇 »
JS数组