事件冒泡与事件捕获

引言:用户在浏览器操作的时候触发的一种行为就叫事件。

每个元素自身都有事件,只不过默认为null(没有事件,事件值就为undefined),当某个事件绑定一个函数之后,用户在操作浏览器的时候触发了这个事件,就会执行这个事件函数。

什么是事件冒泡和捕获呢?

来看一张图(简单明了~)

由上图我们可以得知

事件冒泡就是从目标元素自下而上一直到window(结束)这样一个过程

事件捕获就是从window自上而下一直到目标元素的这样一个过程

一般是先执行捕获,后执行冒泡

关于冒泡:

① DOM0,DOM2都有冒泡行为

② 目标元素和祖先元素绑定同一事件时候,目标元素触发的同时祖先元素也会执行这一事件

来,看个小栗子

   DOM0 事件冒泡 
  window.onclick = function(){ alert('window'); } box1.onclick = function(){ alert('第一个'); }; box2.onclick = function(){ alert('第二个'); }; box3.onclick = function(){ alert('第三个'); }; //点击 box3分别会由下而上 弹第三个,第二个,第一个,window

 

DOM2 事件冒泡
    div1.addEventListener('click',function(){
        alert('red');
    },false);
    div2.addEventListener('click',function(){
        alert('green');
    },false);
    btn.addEventListener('click',function(){
        alert('按钮');
    },false);
     //依次输出 按钮 -> green -> red

以上分别是DOM0和DOM2中的冒泡

 

冒泡的优点可以通过事件对象的target捕获到事件源是哪个元素,从而提高性能

但在开发中也会遇到冒泡带来的弊端(由于冒泡机制,子元素触发事件时候也会影响父元素和祖先元素)

比如以下效果:

    let onOff = true;
    btn.onclick = function(ev){
        if(onOff){
            box.style.display = 'none';
        }else{
            box.style.display = 'block';
        }
        onOff = !onOff;
    }
    document.onclick = function(){
        box.style.display = 'none';
        onOff = false;
    }

 

问题:点击按钮隐藏红色背景,再次点击按钮时候红色背景应该显示。但因为冒泡的特性,执行完按钮的onclick事件函数,还会执行document,所以再次点击按钮是没有反映的

 解决方法:使用stopPropagation() 和 cancelBubble阻止冒泡的发生

    let onOff = true;
    btn.onclick = function(ev){
        if(onOff){
            box.style.display = 'none';
        }else{
            box.style.display = 'block';
        }
        onOff = !onOff;
        // ev.stopPropagation();
        ev.cancelBubble = true; //阻止了btn的onclick的事件不往上面冒泡。
    }

    document.onclick = function(){
        box.style.display = 'none';
        onOff = false;
    }

其中ev.stopPropagation是W3C标准写法,IE低版本不支持

  ev.cancelBubble = true;这个方法虽然不是标准方法,但大多数浏览器都支持...

注意:开发中如果两个元素是嵌套关系就要小心时间冒泡了,名称尽量不要重复

关于捕获:

① DOM0是检测不到捕获的,只有DOM2/3才能捕获到

② addEventListener/removeEventListener中的布尔值默认都是false不捕获

  div1.addEventListener('click',function(){
        alert('red');
    },true);
    div2.addEventListener('click',function(){
        alert('green');
    },true);
    btn.addEventListener('click',function(){
        alert('按钮');
    },true);
    //点击btn依次输出 red -> green -> 按钮

 

事件流(模型)

当一个事件触发时,一般会经历三个过程。即捕获阶段(window由上而下到目标元素)、目标阶段冒泡阶段(目标元素由下而上到window),这么个过程称为事件流(事件模型)

其中目标阶段,他是按照事件绑定的先后顺序,而不是按照先捕获后冒泡这一规则来执行

下面是事件流的一些小测试

       div1.addEventListener('click',function(){
            console.log('red');
        },true);
        div2.addEventListener('click',function(){
            console.log('green');
        },false);
        btn.addEventListener('click',function(){
            console.log('按钮');
        },false);  
        function fn(){  console.log('tred');}
        btn.addEventListener('click',function(){
            console.log('按钮2');
        },false);
        div2.addEventListener('click',function(){
            console.log('tgreen');
        },true);
        div1.addEventListener('click',fn,true);
        div1.addEventListener('click',function(){
            console.log('red');
        },false);
        btn.addEventListener('click',function(){
            console.log('t按钮');
        },true);
        div2.addEventListener('click',function(){
            console.log('green');
        },false);
        btn.addEventListener('click',function(){
            console.log('按钮');
        },false);
//点击btn依次会输出下面结果 //red->tred->tgreen->按钮->按钮2->t按钮->按钮->green->green->red

 

PS:下面说下DOM0和DOM2 (木有DOM1...)

DOM0就是传统的事件,比如以on开头。DOM0检测不到事件捕获行为

DOM2有两个方法来处理事件:绑定 -> addEventListener()、解绑 -> removeEventListener()

绑定事件方法:

/*
第一个参数:不带on的事件名称
第二个参数:事件函数
第三个参数:布尔值,是否捕获。默认false不捕获
*/
ele.addEventListener("不带on的事件名","事件函数","是否捕获")

解绑事件方法:

/*
第一个参数:不带on的事件名称
第二个参数:解绑的事件函数
第三个参数:布尔值,是否捕获。默认false不捕获
*/
ele.removeEventListener("不带on的事件名","解绑事件函数","是否捕获")

除了上面的两种方法,还有IE下面的两个绑定、解绑方法

attachEvent('带on事件名',‘事件函数’)
detachEvent('带on事件名',‘事件函数’)

注意:这里的第一个参数是带on的事件名

posted @ 2018-11-17 09:39  紫诺花开  阅读(384)  评论(0编辑  收藏  举报