zoukankan      html  css  js  c++  java
  • Javascript事件模型(一):DOM0事件和DOM2事件

      javascript事件模型,本文主要有以下内容:

    • DOM0事件模型
    • DOM2事件模型

     一、DOM0事件模型

      早期的事件模型称为DOM0级别。

      DOM0的事件具有极好的跨浏览器优势, 会以最快的速度绑定, 如果你通过DOM2绑定要等到JS运行, DOM0不用, 因为DOM0是写在元素上面;

    直接在dom对象上注册事件名称,就是DOM0写法,比如:

    document.getElementById("test").onclick = function(e){};

      另外一种写法是:

    document.getElementById("test")["onclick"] = function(e){};

      []的形式主要是为了解决属性名不是合法的标识符,比如:object.123肯定报错,但是object["123"]就避免了这个问题,与此同时,[]的写法,也把js写活了,用字符串表示属性名称,可以在运行时动态绑定事件。

      事件被触发时,会默认传入一个参数e,表示事件对象,通过e,我们可以获取很多有用的信息,比如点击的坐标、具体触发该事件的dom元素等等。基于DOM0的事件,对于同一个dom节点而言,只能注册一个,后边注册的同种事件会覆盖之前注册的。例如:

    var btn = document.getElementById("test");
    btn.onmousemove = function(e){
      alert("ok");
    };
    btn["onmousemove"] = function(e){
      alert("ok1");
    };//最后会输出ok1

    事件触发时,this就是指该事件在哪个dom对象上触发。例如:

    var btn = document.getElementById("OK");
    btn.onmousemove = function(e){
      alert(this.id);
    };

     结果输出OK。因为事件就是在id为OK的dom节点上注册的,事件触发时,this当然代表这个dom节点,可以理解为事件是被这个dom节点调用的。

     所以,想解除事件就相当简单了,只需要再注册一次事件,把值设成null,例如:

    var btn = document.getElementById("OK");
    btn.onclick = function(e){
      alert("ok");
    };
    btn.onclick = null;

    原理就是最后注册的事件要覆盖之前的,最后一次注册事件设置成null,也就解除了事件绑定。当然,你也可以用unbind等。

    DOM0事件模型还涉及到直接写在html中的事件。例如:

    1 <div id="test" class="test" onclick="exec();" ></div>

      通过这种方式注册的事件,同样遵循覆盖原则,同样只能注册一个,最后一个生效。

      区别就是,这样注册的事件,相当于动态调用函数(有点eval的意思),因此不会传入event对象,同时,this指向的是window,不再是触发事件的dom对象。


    二、DOM2事件模型

    DOM2支持同一dom元素注册多个同种事件。

    DOM2新增了捕获和冒泡的概念。

    DOM2事件通过addEventListener和removeEventListener管理,当然,这是标准。

    IE8及其以下版本浏览器对应的attachEvent和detachEvent,

    addEventListener有三个参数,分别为:"事件名称", "事件回调", "捕获/冒泡"。举个例子:

    var btn = document.getElementById("test");
    btn.addEventListener("click", function(e){
      alert("ok");
    }, false);

    相比DOM0,去掉了前边的on而已。回调时和DOM0一样,也会默认传入一个event参数,同时this是指触发该事件的dom节点。

    最后一个参数是布尔型,true代表捕获事件,false代表冒泡事件

      意思就是说,某个元素触发了某个事件,最先得到通知的是window,然后是document,依次而入,直到真正触发事件的那个元素(目标元素)为止,这个过程就是捕获。接下来,事件会从目标元素开始起泡,再依次而出,直到window对象为止,这个过程就是冒泡。

      由此可以看出,捕获事件要比冒泡事件先触发。

      有这样的html结构:

    <div id="test" class="test">
    <div id="testInner" class="test-inner"></div>
    </div>

     然后我们在外层div上注册两个click事件,分别是捕获事件和冒泡事件,代码如下:

    var btn = document.getElementById("test");
    //捕获事件
    btn.addEventListener("click", function(e){
      alert("ok1");
    }, true);
    //冒泡事件
    btn.addEventListener("click", function(e){
      alert("ok");
    }, false);

    最后,点击内层的div,先弹出ok1,后弹出ok。结合上边的原理图,外层div相当于图中的body,内层div相当于图中最下边的div,证明了捕获事件先执行,然后执行冒泡事件。

         为什么要强调点击内层的div呢?因为真正触发事件的dom元素,必须是内层的,外层dom元素才有机会模拟捕获事件和冒泡事件,从原理图上就看出了。

         如果在真正触发事件的dom元素上注册捕获事件和冒泡事件呢?

         html结构同上,js代码如下:

    var btnInner = document.getElementById("testInner");
    //冒泡事件
    btnInner.addEventListener("click", function(e){
      alert("ok");
    }, false);
    //捕获事件
    btnInner.addEventListener("click", function(e){
      alert("ok1");
    }, true);

    当然还是点击内层div,结果是先弹出ok,再弹出ok1。理论上应该先触发捕获事件,也就是先弹出ok1,但是这里比较特殊,因为我们是在真正触发事件的dom元素上注册的事件,相当于在图中的div上注册,由图可以看出真正触发事件的dom元素,是捕获事件的终点,是冒泡事件的起点,所以这里就不区分事件了,哪个先注册,就先执行哪个。本例中,冒泡事件先注册,所以先执行。

         这个道理适用于多个同种事件,比如说一下子注册了3个冒泡事件,那么执行顺序就按照注册的顺序来,先注册先执行。例如:

    var btnInner = document.getElementById("testInner");
    btnInner.addEventListener("click", function(e){
      alert("ok");
    }, false);
    btnInner.addEventListener("click", function(e){
      alert("ok1");
    }, false);
    btnInner.addEventListener("click", function(e){
      alert("ok2");
    }, false);

       结果当然是依次弹出ok、ok1、ok2。

       为了进一步理解事件模型,还有一种场景,假如说外层div和内层div同时注册了捕获事件,那么点击内层div时,外层div的事件一定是先触发的,代码如下:

    var btn = document.getElementById("test");
    var btnInner = document.getElementById("testInner");
    
    btnInner.addEventListener("click", function(e){
      alert("ok");
    }, true);
    
    btn.addEventListener("click", function(e){
      alert("ok1");
    }, true);

    结果是先弹出ok1。

         假如外层div和内层div都是注册的冒泡事件,点击内层div时,一定是内层div事件先执行,原理相同。

         细心的读者会发现,对于div嵌套的情况,如果点击内层的div,外层的div也会触发事件,这貌似会有问题!

         点击的明明是内层div,但是外层div的事件也触发了,这的确是个问题。

         其实,事件触发时,会默认传入一个event对象,前边提过了,这个event对象上有一个方法:stopPropagation,通过此方法,可以阻止冒泡,这样外层div就接收不到事件了。代码如下:

    var btn = document.getElementById("test");
    var btnInner = document.getElementById("testInner");
    btn.addEventListener("click", function(e){
      alert("ok1");
    }, false);
    btnInner.addEventListener("click", function(e){
      //阻止冒泡
    e.stopPropagation();
      alert("ok");
    }, false);

    解除事件语法:btn.removeEventListener("事件名称", "事件回调", "捕获/冒泡");

         这和绑定事件的参数一样,详细说明下:

              ·  事件名称,就是说解除哪个事件呗。

              ·  事件回调,是一个函数,这个函数必须和注册事件的函数是同一个。

              ·  事件类型,布尔值,这个必须和注册事件时的类型一致。

         也就是说,名称、回调、类型,三者共同决定解除哪个事件,缺一不可。举个例子:

    var btn = document.getElementById("test");
    //将回调存储在变量中
    var fn = function(e){
      alert("ok");
    };
    //绑定
    btn.addEventListener("click", fn, false);
    //解除
    btn.removeEventListener("click", fn, false);

    要想注册过的事件能够被解除,必须将回调函数保存起来,否则无法解除。

     

  • 相关阅读:
    SpringCloud源码解读
    深度剖析Dubbo源码
    十大经典排序算法
    详细介绍Spring 5的那些新特性与增强
    Spring4.x所有Maven依赖
    常见的接口与类 -- Comparable
    java序列化测试
    什么是Java序列化?为什么序列化?序列化有哪些方式?
    java堆、栈、堆栈的区别
    IntelliJ IDEA常用快捷键汇总
  • 原文地址:https://www.cnblogs.com/qixinbo/p/6885734.html
Copyright © 2011-2022 走看看