zoukankan      html  css  js  c++  java
  • 浏览器事件机制与自定义事件的实现

    一、 0 级 DOM 上的事件和 2 级 DOM 事件机制

    0 级 DOM 上的事件又称原始事件模型,所有的浏览器都支持他,而且是通用的。 2 级 DOM 事件机制又为标准事件模型,除了 ie 其他浏览器都支持( ie9 据说也支持,有待考证), ie 虽然大部分与标准事件模型一样,但有自己专有的事件模型,因此开发人员要实现标准事件模型必须为 IE 写特定的代码,这给程序员增加了负担。

    原始事件模型 

    1.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    2.<html>  
    3.  <head>  
    4.    <title>浏览器0级DOM上的事件</title>  
    5.    <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
    6.  </head>  
    7.     
    8.  <body>  
    9.    <input type="button" value = "点击我" id = "btn">  
    10.  </body>  
    11.</html>  
    12.  
    13.<script type="text/javascript">  
    14.<!--   
    15.var method1 = function(){alert(1)};   
    16.var method2 = function(){alert(2)};   
    17.var method3 = function(){alert(3)};   
    18.document.getElementById("btn").onclick = method1;    
    19.document.getElementById("btn").onclick = method2;    
    20.document.getElementById("btn").onclick = method3;   
    21.//-->  
    22.</script>

    以上书写在各浏览器中都是兼容的,但只有 medhot3 被执行,即同一个对象同一类型的事件只能注册一个处理函数,要想实现注册多个处理函数,需要利用 2 级 DOM 事件机制。

    1.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    2.<html>  
    3.  <head>  
    4.    <title>浏览器2级DOM事件机制</title>  
    5.    <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
    6.  </head>  
    7.     
    8.  <body>  
    9.    <input type="button" value = "点击我" id = "btn">  
    10.  </body>  
    11.</html>  
    12.  
    13.<script type="text/javascript">  
    14.<!--   
    15.var method1 = function(){alert(1)};   
    16.var method2 = function(){alert(2)};   
    17.var method3 = function(){alert(3)};   
    18.  
    19.//执行顺序为method1->method2->method3    
    20.//标准事件模型   
    21.var btn1Obj = document.getElementById("btn");    
    22.btn1Obj.addEventListener("click",method1,false);    
    23.btn1Obj.addEventListener("click",method2,false);    
    24.btn1Obj.addEventListener("click",method3,false);   
    25.  
    26.//执行顺序为method3->method2->method1   
    27.//IE事件模型   
    28.var btn1Obj = document.getElementById("btn");   
    29.btn1Obj.attachEvent("onclick",method1);    
    30.btn1Obj.attachEvent("onclick",method2);    
    31.btn1Obj.attachEvent("onclick",method3);   
    32.  
    33.//-->  
    34.</script> 

    从运行结果来看, ie 和 firefox 下执行的顺序是不一样的

    二、2级DOM事件模型事件的注册与删除

    element.addEventListener(eventType,fn,useCapture); // 注册事件

    element.removeEventListener(eventType,fn, useCapture);// 删除事件 

          可以用 addEventListener() 给同一个对象同一类型的事件注册多个处理函数,但是如果在同一元素上多次注册了一个处理函数,那么第一次注册后的所有注册都将被忽略,但删除该注册函数(调用 removeEventListener() )后可以再重新注册该函数。需要注意的是删除事件, useCapture 的值必须要跟注册时保持一致

    1.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
    2.     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    3.<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
    4.<head>  
    5.<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
    6.<title>浏览器事件机制——注册和删除事件</title>  
    7.<style>  
    8.     div {border:1px  solid blue;}   
    9.     div#div1 {padding:40px;background-color:#aaaaaa;}   
    10.     div#div2 {padding:40px;background-color:#bbbbbb;}   
    11.     div#div3 {padding:40px;background-color:#cccccc;}   
    12.</style>  
    13.</head>  
    14. <body>  
    15.<div id="div1" style="100px;height:100px;" >  
    16.      我是老大, 点击我添加老三的click事件   
    17.</div>  
    18.<br/>  
    19.<div id="div2" style="100px;height:110px;" >  
    20.    我是老二, 点击我删除老三的click事件           
    21.</div>  
    22.<br/>  
    23.<div id="div3" style="100px;height:100px;" >  
    24.    我是老三,是否有click事件,老大老二说了算,呵呵   
    25.</div>  
    26.<script>  
    27.    function click1() {   
    28.        alert("I am div1,add div3 event");   
    29.        if(window.addEventListener){   
    30.            div3.addEventListener("click", click3, false);   
    31.        }else if (window.attachEvent){   
    32.            div3.attachEvent("onclick", click3);   
    33.        }   
    34.    }   
    35.    function click2() {   
    36.        alert("I am div2,remove div3 event");   
    37.        if(window.addEventListener){   
    38.            div3.removeEventListener("click", click3, false);   
    39.        }else if (window.attachEvent){   
    40.            div3.detachEvent("onclick", click3);   
    41.        }   
    42.    }   
    43.    function click3() {   
    44.        alert("I am div3");   
    45.    }   
    46.       
    47.    var div1 = document.getElementById("div1");   
    48.    var div2 = document.getElementById("div2");   
    49.    var div3 = document.getElementById("div3");   
    50.       
    51.    if(window.addEventListener){   
    52.        div1.addEventListener("click", click1, false);   
    53.        div2.addEventListener("click", click2, false);   
    54.    }else if (window.attachEvent){   
    55.        div1.attachEvent("onclick", click1);   
    56.        div2.attachEvent("onclick", click2);   
    57.    }   
    58.</script>       
    59.</body>  
    60.</html>  

    三、2级DOM事件冒泡模型(Bubble Model) 

          在2级DOM事件模型中,事件传播分三个阶段进行,即捕获阶段(capturing)、目标阶段和冒泡阶段(bubbling)。在捕获阶段,事件从Document对象沿着文档树向下传播给目标节点,如果目标的任何一个祖先(不是目标本身)专门注册了捕获事件句柄,那么在事件传播过程中,就会运行这些句柄,在冒泡阶段,事件将从目标元素向上传播回或气泡回Document对象的文档层次。虽然所有事件都受事件传播的捕获阶段的支配,但并非所有类型的事件都起泡。

          在注册事件时,useCapture参数确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true,则侦听器只在捕获阶段处理事件,而不在目标或冒泡阶段 处理事件。 如果useCapture 为 false,则侦听器只在目标或冒泡阶段处理事件。 要在所有三个阶段都侦听事件,需调用两次 addEventListener,一次将 useCapture 设置为 true,第二次再将useCapture 设置为 false。

    1.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
    2.     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    3.<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
    4.<head>  
    5.<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
    6.<title>浏览器事件机制——冒泡处理</title>  
    7.<style>  
    8.     div {border:1px  solid blue;}   
    9.     div#divGrandpa {padding:40px;background-color:#aaaaaa;}   
    10.     div#divFather {padding:40px;background-color:#bbbbbb;}   
    11.     div#divSon {padding:40px;background-color:#cccccc;}   
    12.</style>  
    13.</head>  
    14. <body>  
    15.<div id="divGrandpa" style="300px;height:200px;" >  
    16.       <div id="divFather" style="200px;height:120px;" >  
    17.            <div id="divSon" style="100px;height:40px;" >  
    18.                点击我   
    19.            </div>  
    20.       </div>  
    21.</div>  
    22.<script>  
    23.    function showSon() {   
    24.        alert("I am son");   
    25.    }   
    26.    function showFather() {   
    27.        alert("I am father");   
    28.    }   
    29.    function showGrandpa() {   
    30.        alert("I am Grandpa");   
    31.    }   
    32.    var grandpa = document.getElementById("divGrandpa");   
    33.    var father = document.getElementById("divFather");   
    34.    var son = document.getElementById("divSon");   
    35.    if(window.addEventListener){   
    36.        grandpa.addEventListener("click", showGrandpa, false);   
    37.        father.addEventListener("click", showFather, false);   
    38.        son.addEventListener("click", showSon, false);   
    39.    }else if (window.attachEvent){   
    40.        grandpa.attachEvent("onclick", showGrandpa);   
    41.        father.attachEvent("onclick", showFather);   
    42.        son.attachEvent("onclick", showSon);   
    43.    }   
    44.</script>       
    45.</body>  
    46.</html>  

    从运行结果来看,对于ie,在ie(ie8之前的版本,包括ie8)中当点击son节点时,会分别弹出I am son、I am father和I am Grandpa,即事件最先被底层的结点触发,再逐渐上传,直到最外层的结点,冒泡方式为儿子——>父亲的模式;在Firefox等支持标准事件模型的浏览器中,跟addEventListener的Capture参数有关,当设置为true时,为捕获模式,事件会从最顶层的结点往下传输,即 父亲——>儿子的传播模式。当设为false(默认值)时,则会按冒泡模式传递事件。另外由于ie9即支持window.attachEvent,又支持window.addEventListener,所以会根据代码的书写来运行其效果的。

    四、如何停止事件的传递

    在IE浏览器中可以调用以下代码
    event.cancelBubble = true;
    在Firefox等遵循W3C规范的浏览器中,可以调用以下代码
    e.stopPropagation();

    调用以上代码后可以终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点(即不再进一步传播)。
    该方法(属性)将停止事件的传播,阻止它被分派到其他 Document 节点。在事件传播的任何阶段都可以调用它。注意,虽然该方法不能阻止同一个 Document 节点上的其他事件句柄被调用,但是它可以阻止把事件分派到其他节点。

    1.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
    2.     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    3.<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
    4.<head>  
    5.<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
    6.<title>浏览器事件机制——停止事件的进一步传递</title>  
    7.<style>  
    8.     div {border:1px  solid blue;}   
    9.     div#divGrandpa {padding:40px;background-color:#aaaaaa;}   
    10.     div#divFather {padding:40px;background-color:#bbbbbb;}   
    11.     div#divSon {padding:40px;background-color:#cccccc;}   
    12.</style>  
    13.</head>  
    14. <body>  
    15.<div id="divGrandpa" style="300px;height:200px;" >  
    16.       <div id="divFather" style="200px;height:120px;" >  
    17.            <div id="divSon" style="100px;height:40px;" >  
    18.                点击我   
    19.            </div>  
    20.       </div>  
    21.</div>  
    22.  
    23.<script>  
    24.    function showSon(e) {   
    25.        alert("I am son");   
    26.    }   
    27.    function showFather(e) {   
    28.        //IE把event对象作为window对象的一个属性,而W3C把event对象作为处理程序的一个参数   
    29.        ee = e || event;   
    30.        if(e.stopPropagation){   
    31.            e.stopPropagation();   
    32.        }else{   
    33.            e.cancelBubble = true;   
    34.        }   
    35.        alert("I am father");   
    36.    }   
    37.    function showGrandpa(e) {   
    38.        alert("I am Grandpa");   
    39.    }   
    40.    var grandpa = document.getElementById("divGrandpa");   
    41.    var father = document.getElementById("divFather");   
    42.    var son = document.getElementById("divSon");   
    43.    if(window.addEventListener){   
    44.        grandpa.addEventListener("click", showGrandpa, false);   
    45.        father.addEventListener("click", showFather, false);   
    46.        son.addEventListener("click", showSon, false);   
    47.    }else if (window.attachEvent){   
    48.        grandpa.attachEvent("onclick", showGrandpa);   
    49.        father.attachEvent("onclick", showFather);   
    50.        son.attachEvent("onclick", showSon);   
    51.    }   
    52.</script>       
    53.</body>  
    54.</html>  

    五、自定义事件 

    1、不带参数事件处理,也是最简单的事件设计模式

    最简单的一种模式是将一个类的方法成员定义为事件,通常是一个空函数,当程序需要处理该事件时,再进行扩充该事件接口。比如:

    1.function Class1(){   
    2.        //构造函数   
    3.    }   
    4.    Class1.prototype = {   
    5.        show : function(){   
    6.            this.onShow();//触发onShow事件   
    7.        },   
    8.        onShow : function(){}//定义事件接口   
    9.    }   
    10.    //创建class1实例   
    11.    var obj = new Class1();   
    12.    //创建obj的onShow事件处理程序   
    13.    obj.onShow = function(){   
    14.        alert('onshow event');   
    15.    }   
    16.    //调用obj的show方法   
    17.    obj.show();  

    2、给事件处理程序传递参数

    1.//将有参数的函数封装为无参数的函数   
    2.    function createFunction(obj, strFn){   
    3.        obj = obj || window;   
    4.        var args = [];   
    5.        for(var i = 2; i < arguments.length; i++){   
    6.            args.push(arguments[i]);   
    7.        }   
    8.        return function(){   
    9.            //该语句相当于obj[strFn](args[0],args[1],...);   
    10.            obj[strFn].apply(obj,args);   
    11.        }   
    12.    }   
    13.    //定义类 Class1   
    14.    function Class1(){   
    15.        //构造函数   
    16.    }   
    17.    Class1.prototype = {   
    18.        show : function(){   
    19.            this.onShow();//触发onShow事件   
    20.        },   
    21.        onShow : function(){}//定义事件接口   
    22.    }   
    23.    //创建class1实例   
    24.    var obj = new Class1();   
    25.    //创建obj的onShow事件处理程序   
    26.    function objOnShow(userName){   
    27.        alert('hello, ' + userName);   
    28.    }   
    29.    var userName = 'xiaowang';   
    30.    //绑定obj的onShow事件   
    31.    obj.onShow = createFunction(null,'objOnShow',userName);   
    32.    //调用obj的show方法   
    33.    obj.show();  

    在以上代码中,将变量userName作为参数传递给了objOnShow事件处理程序。事实上,obj.onShow 得到的事件处理程序并不是objOnShow,而是由createFunction返回的一个无参函数

    3、自定义事件支持多绑定

    1.//定义类 Class1   
    2.    function Class1(){   
    3.        //构造函数   
    4.    }   
    5.    Class1.prototype = {   
    6.        show : function(){   
    7.            //如果有事件绑定则循环onshow数组,触发该事件   
    8.            if(this.onshow){   
    9.                for(var i = 0, len = this.onshow.length; i < len; i++){   
    10.                    this.onshow[i]();//调用事件处理程序   
    11.                }    
    12.            }   
    13.        },   
    14.        addEventOnShow : function (_eHandler){   
    15.            this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用   
    16.            this.onshow.push(_eHandler);   
    17.        }   
    18.    }   
    19.    //创建class1实例   
    20.    var obj = new Class1();   
    21.    //事件一   
    22.    function onShow1(){   
    23.        alert('event1');   
    24.    }   
    25.    //事件二   
    26.    function onShow2(){   
    27.        alert('event2');   
    28.    }   
    29.    //绑定事件   
    30.    obj.addEventOnShow(onShow1);   
    31.    obj.addEventOnShow(onShow2);   
    32.    //调用obj的show方法   
    33.    obj.show();  

    4、自定义事件支持带参数的多绑定

    1.//将有参数的函数封装为无参数的函数   
    2.    function createFunction(obj, strFn){   
    3.        obj = obj || window;   
    4.        var args = [];   
    5.        for(var i = 2; i < arguments.length; i++){   
    6.            args.push(arguments[i]);   
    7.        }   
    8.        return function(){   
    9.            //该语句相当于obj[strFn](args[0],args[1],...);   
    10.            obj[strFn].apply(obj,args);   
    11.        }   
    12.    }   
    13.    //定义类 Class1   
    14.    function Class1(){   
    15.        //构造函数   
    16.    }   
    17.    Class1.prototype = {   
    18.        show : function(){   
    19.            //如果有事件绑定则循环onshow数组,触发该事件   
    20.            if(this.onshow){   
    21.                for(var i = 0, len = this.onshow.length; i < len; i++){   
    22.                    this.onshow[i]();//调用事件处理程序   
    23.                }    
    24.            }   
    25.        },   
    26.        addEventOnShow : function (_eHandler){   
    27.            this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用   
    28.            this.onshow.push(_eHandler);   
    29.        }   
    30.    }   
    31.    //创建class1实例   
    32.    var obj = new Class1();   
    33.    //创建obj的onShow事件处理程序   
    34.    function objOnShow(userName){   
    35.        alert('hello, ' + userName);   
    36.    }   
    37.    //事件一   
    38.    var  userName1 = 'xiaowang';   
    39.    var  onShow1 = createFunction(null,'objOnShow',userName1);   
    40.    //事件一   
    41.    var  userName2 = 'xiaoli';   
    42.    var  onShow2 = createFunction(null,'objOnShow',userName2);   
    43.    //绑定事件   
    44.    obj.addEventOnShow(onShow1);   
    45.    obj.addEventOnShow(onShow2);   
    46.    //调用obj的show方法   
    47.    obj.show();  

    以上实现把带参数和多绑定结合在一起,还可以增加一个removeEventOnShow来删除已注册的事件。

    六、把对象注册为事件句柄

          在编写面向对象的JavaScript程序时,如果想用对象作为事件句柄,那么可以使用如下的函数来注册它们:

    function registerObjectEventHandler(element,eventtype,listener,captures){
        element.addEventListener(eventtype,
            function(event) {listener.handleEvent(event);},captures);
    }

           用这个函数可以把任何对象注册为事件句柄,只要它定义了handleEvent()方法。Firefox(以及其他基于Mozilla代码的浏览器)允许直接把定义了handleEvent()方法的事件监听器对象传递给addEventListener()方法而不是函数引用。对于这些浏览器来说,不需要我们刚才给出的特殊注册函数。 

    请看下面的例子

    1.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    2.<html>  
    3.  <head>  
    4.    <title>把对象注册为事件句柄</title>  
    5.    <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
    6.  </head>  
    7.     
    8.  <body>  
    9.    <input type="button" value = "点击我" id = "btn">  
    10.  </body>  
    11.</html>  
    12.  
    13.<script type="text/javascript">  
    14.<!--   
    15.    var EventHandler = function(){}   
    16.    EventHandler.prototype.handleEvent = function(event){   
    17.        alert('用对象作为事件句柄,只要实现该对象的方法handleEvent即可');   
    18.        alert(event.type);   
    19.    }   
    20.    var objectHandler = new EventHandler();   
    21.    var btn1Obj = document.getElementById("btn");    
    22.    if(window.addEventListener){   
    23.        btn1Obj.addEventListener("click",objectHandler,false);    
    24.    }else if (window.attachEvent){   
    25.        //btn1Obj.attachEvent("onclick",objectHandler);//调用失败,说明不支持把对象注册为事件句柄   
    26.        //btn1Obj.attachEvent("onclick",objectHandler.handleEvent);   
    27.        registerObjectEventHandler(btn1Obj,"onclick",objectHandler);   
    28.    }   
    29.  
    30.    /**   
    31.     * 对于不支持把对象注册为事件句柄的浏览器,可以调用以下方法来实现   
    32.     */   
    33.    function registerObjectEventHandler(element,eventtype,listener,captures){   
    34.        if(window.addEventListener){   
    35.            element.addEventListener(eventtype,   
    36.                function(event) {listener.handleEvent(event);},captures);   
    37.        }else if (window.attachEvent){   
    38.            element.attachEvent(eventtype,   
    39.                function(event) {listener.handleEvent(event);});   
    40.        }   
    41.    }   
    42.  
    43.//-->  
    44.</script> 
  • 相关阅读:
    docker知识3---镜像
    docker知识2---docker简介
    docker知识1---容器背景
    docker故障排查
    linux故障处理--ssh故障
    sqlalchemy的一行代码更新数据库
    Python内置函数和高阶函数(map,filter,reduce, sorted,enumerate, zip,单行条件语句 )
    Mysql略复杂命令总结
    pip的使用
    Linux的基础命令
  • 原文地址:https://www.cnblogs.com/answercard/p/3835606.html
Copyright © 2011-2022 走看看