• JS中事件绑定函数,事件捕获,事件冒泡


    1 事件绑定:事件与函数绑定以及怎么取消绑定

    1.1 元素.onclick这种形式,如下:

    1         <div id="div1">aaa</div>
    2         
    3         <script type="text/javascript">
    4             var oDiv1 = document.getElementById('div1');
    5             oDiv1.onclick = function(){
    6                 alert(this.innerHTML); //将会弹出aaa
    7             }
    8         </script>

    这种绑定不存在什么兼容性问题,但是如果想绑定2个事件,就麻烦了。上面的js代码改成如下:

    1             var oDiv1 = document.getElementById('div1');
    2             oDiv1.onclick = function(){
    3                 alert(this.innerHTML); //将会弹出aaa
    4             }
    5             oDiv1.onclick = function(){
    6                 alert(this.nodeType);
    7             }

    我们给oDiv1后面又绑定了onclick,前面的那个alert(this.innerHTML)就会失效。

    这种方式绑定了,后面的代码需要取消这个绑定,哪怎么取消呢

    1 //取消刚才的绑定
    2 oDiv1.onclick = null;

    当然,这里只是很简单的两个函数例子,可以很容易将2个onclick合并,但是如果是多人合作,代码多,结构复杂,还是看看其他的事件和函数绑定的方式吧。 

    1.2 元素.attachEvent 和 元素.addEventListener进行事件和函数绑定和取消,如下:

    这种方式存在浏览器兼容性问题

    /* 总结
    ie6/7/8 :obj.attachEvent(事件名称,事件函数);
        1.没有捕获(事件捕获在后面讲,是和冒泡相反的,IE不支持)
        2.事件名称带有on,如这里是onclick 
        3.事件函数执行的顺序是反着的,后绑定的那个函数先执行
        4.事件函数中this指向window,这不是本意,带来了不方便
    标准浏览器及高版本IE:obj.addEventListener(事件名称,事件函数,是否捕获);
        1.有捕获
        2.事件名称不带on
        3.事件执行的顺序是正常的
        4.this触发该事件的对象
    */

    上面的代码继续改,如下:

    下面的代码只能在IE6.7.8.9下运行,但是ie9也有addEventListener方法,就是IE9下attachEvent和addEventListener都行

     1         <div id="div1">aaa</div>
     2         
     3         <script type="text/javascript">
     4             var oDiv1 = document.getElementById('div1');
     5             //改的是这里,事件和函数进行绑定
     6             oDiv1.attachEvent('onclick',function(){
     7                 //这里的this变成了window,也不能用this.innerHTML了
     8                 alert(oDiv1.innerHTML);                
     9             });
    10             oDiv1.attachEvent('onclick',function(){
    11                 alert(oDiv1.nodeType);                
    12             });            
    13         </script>

    再看看标准浏览器,如谷歌火狐浏览下怎么写

     1         <div id="div1">aaa</div>
     2         
     3         <script type="text/javascript">
     4             var oDiv1 = document.getElementById('div1');
     5             //改的是这里,事件和函数进行绑定,IE.6.7.8不支持addEventListener
     6             oDiv1.addEventListener('click',function(){
     7                 //在标准浏览器下,this还是只oDiv1
     8                 alert(this.innerHTML);                
     9             });//addEventListener第三个参数没写,默认:冒泡
    10             oDiv1.addEventListener('click',function(){
    11                 alert(this.nodeType);                
    12             });            
    13         </script>

    存在兼容性问题,我们封装一个函数进行事件绑定操作

     1         <div id="div1">aaa</div>
     2         
     3         <script type="text/javascript">
     4             var oDiv1 = document.getElementById('div1');
     5             
     6             //改的是这里,封装了一个bind函数来解决
     7             function bind(obj,evname,fn){
     8                 if(obj.addEventListener){ //IE9+、谷歌、火狐等
     9                     obj.addEventListener(evname,fn);
    10                 }else{//IE6.7.8,虽然IE9也支持attachEvent,但是IE9用上面的addEventListener了
    11                     obj.attachEvent('on'+evname,function(){
    12                         fn.call(obj);
    13                     });
    14                 }
    15             }
    16 
    17             //使用
    18             bind(oDiv1, 'click', function(){
    19                 alert(this.innerHTML);    
    20             });
    21             bind(oDiv1, 'click', function(){
    22                 alert(this.nodeType);    
    23             });
    24         </script>

    那怎么取消掉这种方式绑定呢?

    /*
                老ie : obj.detachEvent(事件名称,事件函数);
                标准    : obj.removeEventListener(事件名称,事件函数,是否捕获);
                */
    /**
    
    上面事件绑定的时候,我没有给函数命名,是个匿名函数。那匿名函数看着是一样的代码,确是不同的对象,就没有办法取消的了。
    */ oDiv1.addEventListener('click', fn1, true); // 绑定的时候是true,取消的时候,最后那个参数也需要为true,为false则取消不掉 // 下面fn1改为fn2不报错,虽然document没有绑定fn2,但是改为fn3就不行了,报错:不存在fn3这个函数 oDiv1.removeEventListener('click', fn1, true);
                var oDiv1 = document.getElementById('div1');
                
                oDiv1.addEventListener('click',function(){
                    alert(this.innerHTML);
                });
        //取消不了,因为匿名函数没有抓手,得用有名字的函数
                oDiv1.removeEventListener('click',function(){
                    alert(this.innerHTML);
                });

    而上面这个换成下面这样就起效果。

                var oDiv1 = document.getElementById('div1');
                
                oDiv1.addEventListener('click',fn1});
                //这样点击就没有效果,因为取消掉了
                oDiv1.removeEventListener('click',fn1);
                
                function fn1(){
                    alert(this.innerHTML);
                }    

    2. 事件冒泡和取消冒泡

    2.1 事件冒泡

    比如:div1里面有div2,div2里又有div3。那么当点击div3时,系统会认为div2也点了,div1也点了,body也点了,document也点了,一直到window。一层一层往外冒泡。具体内容如下:

     1     <style>
     2         div   {padding: 40px;}
     3         #div1 {background:red}
     4         #div2 {background:green}
     5         #div3 {background:blue; position: absolute; top: 300px;}
     6     </style>
     7     <div id="div1">
     8         <div id="div2">
     9             <div id="div3"></div>
    10         </div>
    11     </div>
    12     <script>
    13         window.onload = function() {
    14     
    15             //事件冒泡 : 当一个元素接收到事件的时候,会把他接收到的所有传播给他的父级,一直到顶层window.事件冒泡机制
    16             /*
    17              实例来说:
    18              下面的例子,从html中来看,div3是div2的子元素,div2又是div1的子元素,div1是body的子元素
    19             当div3发生事件的时候(这里是onclick),浏览器会认为div2也发生了该事件,div1也发生了该事件
    20             就是事件从子级朝父级冒泡
    21             同时,注意,div3加上绝对定位,视觉上在div1中也不行,点击div3,div1还是会认为被点击了
    22              
    23             */
    24     
    25             var oDiv1 = document.getElementById('div1');
    26             var oDiv2 = document.getElementById('div2');
    27             var oDiv3 = document.getElementById('div3');
    28     
    29             function fn1() {
    30                 alert(this.id);
    31             }
    32     
    33             //事件函数绑定
    34             oDiv1.onclick = fn1; //告诉div1,如果他接收到了一个点击事件,那么他就去执行fn1
    35             oDiv2.onclick = fn1;
    36             oDiv3.onclick = fn1;
    37     
    38         }
    39     </script>

    那有个问题,在不知道事件冒泡这个事情的时候,也正常用,没出什么问题呀?

    答:因为大多数时候,子级点击发生事件就是我们最终的效果,会传到父级身上产生事件,但是父级没绑定函数,也就没有反应,我们也就感觉不到干嘛要学到用到这一块的东西。但是,子级和父级产生冲突的时候,就需要了解原因,取消冒泡了。

    子级和父级同一事件冲突举例:

    点击某按钮,希望弹出一个div层,而点击页面中任何除去div层一部分,希望div层隐藏。这里,点击按钮和div层都会冒泡到document,就等同于告诉document,点击后弹出div层,而后面点击页面中任何一部分,又希望隐藏div层,document接到完全矛盾的命令,怎么办?只能是点击按钮和div层的时候,取消冒泡,这样,document就不觉得自己被点击了。怎么取消冒泡?如下:

    2.2 取消冒泡:事件绑定函数中加上ev.cancelBubble = true;

    上面的代码中,fn1修改下就可以了,如下:

    1             function fn1(ev) {
    2                 var ev = ev || window.event;
    3                 ev.cancelBubble = true;
    4                 alert(this.id);
    5             }

    fn1更换过后,点击div3时,由原来的弹出3次,分别是div3,div2,div1,变成了弹出1次,div3,为何?因为取消冒泡了,div2和div1不认为自己被点击了,也就不调用函数了,也就不弹了。

    3 事件捕获

    3.1 事件捕获

    事件捕获是一个和事件冒泡反着的过程。

    冒泡是:点了儿子,儿子觉得被点了(如果绑定了函数,函数执行),然后老子也觉得被点了(如果绑定了函数,函数执行)。

    捕获是:点了儿子,老子先觉得被点了(如果绑定了函数,函数执行),然后儿子也觉得被点了(如果绑定了函数,函数执行)。

    顺序不同,老旧的IE浏览器没有事件捕获的概念,只有冒泡。元素.onclick这种形式的绑定时,就是事件冒泡。

    在火狐,谷歌,IE9+浏览器下:

    //第三个参数,默认false,也就是冒泡。如果第三个参数为true,则为事件捕获
    obj.removeEventListener(事件名称,事件函数,是否捕获);

    3.2 取消捕获

    我认为捕获没有取消的概念,

    事件冒泡时候冲突是这样的。子级点击要往东,父级点击要往西。点击子级的时候,往东,但是父级认为自己也点击了,结果是往西。而点击父级非子级区域时,还是往西。于是矛盾了,只能往西,实现不了往东。

    事件是捕获的话,点击,父级要往西,子级要往东,结局:往东。而点击父级的非子级区域时候,往西。往东往西都可以实现。

    所以,捕获没有向冒泡那样,还需要取消冒泡,这个概念。这地方是我自己的理解,不一定到位,暂时存疑吧!

  • 相关阅读:
    Lombok注解实现final属性的构造注入
    Spring事务传播行为控制
    git分支操作
    Java泛型、泛型方法详解
    规则校验功能设计思路
    Idea下将Maven项目打成公共依赖jar包
    结合Spring注册InitializingBean接口实现策略注册
    基于枚举类的策略模式实现
    python利用smtp协议发送邮件
    html常用邮箱格式总结 在页面添加邮箱
  • 原文地址:https://www.cnblogs.com/html55/p/10262371.html
Copyright © 2020-2023  润新知