js DOM事件模型浅析

事件模型按照DOM规范来说是有两种:DOM0级事件模型以及DOM2级事件模型。但由于IE浏览器的特殊性,IE8及以下浏览器不支持DOM2及事件模型,所以需要单独列出一项IE事件模型。尽管现在IE8及以下版本浏览器可以不作为兼容性考虑对象,但还是有必要说一下。

1.DOM0级事件模型

DOM0级事件模型是最早的事件模型,也叫做原始事件模型,所有浏览器都支持。

这种事件模型比较简单,绑定事件的形式有两种:

(1)直接在内联元素上绑定

<body>
    <!-- DOM0级事件模型:1.直接在内联元素上绑定事件 -->
    <div class="event" onclick="fun1()">click me</div>
    <script>
        function fun1(){
            alert('you have clicked me !')
        }
    </script>
</body>

(2)在js中获取元素节点后绑定事件

    <!-- DOM0级事件模型:2.在js中获取元素节点后绑定事件 -->
    <div class="event">click me</div>
    <script>
        function fun1(e){
            alert('you have clicked me !')
        }
        document.querySelector('.event').onclick = fun1
    </script>

解除事件绑定方法:将元素的绑定事件置空

document.querySelector('.event').onclick = null

DOM0级事件模型的特点是:

(1)每个元素只能绑定一个相同类型事件监听器函数,如果绑定了多个,那么后面的会覆盖前面的。原因是,点击事件onclick是元素节点对象的一个属性,属性值是唯一的。比如,给body元素绑定两个click事件类型的监听器函数:document.body.onclick = fun1, document.body.onclick = fun2,但点击的时候只有fun2会被触发执行。

(2)没有捕获的概念,只有默认的冒泡。我看网上很多介绍DOM0级事件的,说DOM0级事件模型没有事件流的概念,实际上它是有的,只不过没有捕获事件流,只有冒泡事件流,如果类比DOM2级事件模型的说法,DOM0级事件模型包括两个阶段:目标阶段、冒泡阶段。例子如下:

    <!-- DOM0级事件模型:2.在js中获取元素节点后绑定事件 -->
    <div class="event">
        click me 
        <button class="button">button</button>
    </div>
    <script>
        function fun1(e){
            console.log('you have clicked me !')
        }
        function fun2(e){
            console.log('clicked button !')
        }
        document.querySelector('.event').onclick = fun1
        document.querySelector('.button').onclick = fun2
    </script>

点击button按钮,控制台打印如上图所示,先触发了button按钮绑定的事件,这是target目标元素,这就是上边说的目标阶段,然后就是冒泡阶段,冒泡到父元素,发现父元素也注册了click事件,那就触发了这个事件监听函数。

2. DOM2级事件模型

为啥直接到DOM2级事件模型了呢,难道没有DOM1级事件模型吗?原因是1998年10月1日W3C DOM标准制定了DOM级别1标准,但是1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。但是DOM2级标准里规定了新的事件模型,成为DOM2级事件模型。

DOM2级事件模型通过addEventListener(eventType,handler,useCapture)、removeEventListener(eventType,handler,useCapture)方法进行添加、删除事件处理程序。都接受三个参数:

eventType: 事件类型,比如click、mouseover等类型

handler:事件处理程序

useCapture:是否在捕获阶段触发事件,为true表示在捕获阶段触发事件处理程序,为false表示在冒泡阶段触发事件处理程序

DOM2级事件模型事件流包括三个阶段:

(1)捕获阶段:这个阶段,事件从Document对象沿着文档树向下传播到目标节点。如果中间任何一个祖先节点注册了和目标节点相同类型的事件监听函数,那么在事件传播的过程中就会执行这些函数。可以看个例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        .event{
            color: red;
        }
    </style>
</head>
<body>
    <div class="event">
        click me
        <button class="button">button</button>
    </div>
    <script>
        function fun1(e){
            console.log('you have clicked me !')
        }
        function fun2(e){
            console.log('clicked button !')
        }
         // 开启捕获,事件会在捕获阶段被触发
        document.querySelector('.event').addEventListener('click', fun1, true)
        document.querySelector('.button').addEventListener('click', fun2, true)
    </script>
</body>
</html>

控制台打印的日志日志说明了问题,事件流从HTML节点开始往下捕获,到div.event节点发现有click类型的事件,就执行绑定的事件监听函数,所以控制台先打印‘you have clicked me !’,然后再接着往下,传播到目标元素button之前都是捕获阶段,直到传播到目标元素button,此时到了事件流的目标阶段,发现有绑定click类型的监听函数,就会执行该监听函数,所以控制台打印‘clicked button !’。然后是从目标节点向上冒泡,但是在冒泡阶段都不会触发任何click类型的事件,因为addEventListener方法的第三个参数是true。

(2)目标阶段:事件处于目标元素自身,直接注册在目标上的事件监听函数将执行,如果没有就不执行。

(3)冒泡阶段:这个阶段,事件将从目标节点向上传播回Document对象(与capturing相反的阶段)。如果中间任何一个祖先节点注册了和目标节点相同类型的事件监听函数,那么在事件传播的过程中就会执行这些函数。

    <div class="event">
        click me
        <button class="button">button</button>
    </div>
    <script>
        function fun1(e){
            console.log('you have clicked me !')
        }
        function fun2(e){
            console.log('clicked button !')
        }
        // 关闭捕获,事件会在冒泡阶段被触发
        document.querySelector('.event').addEventListener('click', fun1, false)
        document.querySelector('.button').addEventListener('click', fun2, false)
    </script>

可以看到控制台打印的日志顺序与捕获阶段相反,整个过程是:事件流从HTML节点开始往下捕获,但是所有click事件不会在捕获阶段被触发,直到目标阶段,button元素绑定的事件被触发,所以控制台先打印‘clicked button !’,然后向上冒泡,传播到div.event节点,触发绑定的事件监听函数,打印‘you have clicked me !’。

 

阻止事件传播

如果不想让事件继续传播,可以切断传播,方式是调用事件对象的stopPropagation()方法。

    <!-- DOM2级事件模型:阻止事件传播 -->
    <div class="event">
        click me
        <button class="button">button</button>
    </div>
    <script>
        function fun1(e){
            console.log('you have clicked me !')
        }
        function fun2(e){
            // 阻止事件进一步传播
            e.stopPropagation()
            console.log('clicked button !')
        }
        // 关闭捕获,事件会在冒泡阶段被触发
        document.querySelector('.event').addEventListener('click', fun1, false)
        document.querySelector('.button').addEventListener('click', fun2, false)
    </script>

可以看到只触发了button上的事件,没有触发div.event节点上的事件。

DOM2级的Event对象的target和currentTarget属性

用addEventListener添加的事件监听函数,在被调用的时候js会传给他一个Event对象,Event对象有两个属性很容易混淆,就是target和currentTarget属性。target属性就是指向目标节点,currentTarget属性是动态的,指向当时触发事件时的节点,只有处于目标阶段时,target和currentTarget才相同,都指向目标节点。还用上面的例子说明:

    <div class="event">
        click me
        <button class="button">button</button>
    </div>
    <script>
        function fun1(e){
            console.log('you have clicked me !')
        }
        function fun2(e){
            console.log('clicked button !')
        }
        // 关闭捕获,事件会在冒泡阶段被触发
        document.querySelector('.event').addEventListener('click', fun1, false)
        document.querySelector('.button').addEventListener('click', fun2, false)
    </script>

点击button按钮,先执行目标节点button的事件监听函数,此时Event对象的内容如下:

可以看到eventPhase:2表示处于目标阶段,此时target和currentTarget都指向目标节点button。然后执行div.event节点的监听函数,传入的事件对象如下:

可以看到eventPhase:3表示处于冒泡阶段,此时target指向目标节点button,而currentTarget则指向当前正在发生事件的节点div.event节点。

3.IE事件模型

IE事件模型和DOM0级事件模型基本上是一样的,共有两个过程:

(1)目标阶段

(2)冒泡阶段

但是绑定和移除事件的方法是attachEvent(eventType, handler)。

eventType: 事件类型,这里需要注意,需要加上'on'前缀,比如:'onclick',

handler: 事件处理程序

还有一个地方需要注意:IE不把事件对象传给事件处理函数,而是作为window对象的一个属性,即通过window.event访问事件对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值