JavaScript欲速则不达——基本处理事件详解和阻止事件传播

事件就是用户或者浏览器自身执行的某种动作。诸如click、load、和scroll等等,都是事件的名字。而响应某个事件的函就叫做事件处理程序(或事件侦听器)。事件处理程序的名字都是以”on“开头,因此ckick事件的事件处理程序就是onclick,load的事件处理程序就是onload。

页面中添加事件的有几种方式

  1. 直接将代码写在HTMl上

    <div onclick="alert('Hello World');">Nowamagic</div>

    代码少的还勉强可以,代码多的话就哭了写页面的哥们了。

  2. 定义一个函数,分配给html元素

    <script type="text/javascript">
        function clk(){}
    </script>
    //.....
    <div onclick="clk()">Div2 Element</div>
    

    这样做虽然能减少html代码上的js量,但是这样子做有几个缺点:用户可能在HTMl代码已经出现在页面上,但是js事件函数可能还没加载进来的情况下就点击了事件对象元素,从而导致错误;还有就是HTML与javascript代码还是未分离完全,如果要修改就要改HTML代码和javascript代码两处

  3. HTML与javascript代码完全分离

    document.getElementById('myButton').onclick = function(){
        alert('Hello!');
    }
    <div id="myButton">点击按钮/div>
    

    它只需要HTML元素提供一个id属性(或其它能获取该元素对象的方式),就可以实现事件的注册。真正做到HTML与javascript代码完全分离,结构与行为完全分离的事件处理方法。

如何监听事件

DOM Leavl 0

DOM Leavl 0是最早的事件处理形式,它既可以直接写在HTMl上,也可以把一个函数分配给一个事件处理程序。然而,这种方式给一个元素的同一事件只允许一个处理器。因此,我们还要继续完善。

W3C DOM Leavl 2 – 事件监听器

通过W3C DEMO Leavl 2事件处理,我们不会直接把一个函数分配给一个事件处理程序;相反,我们将新函数添加一个事件监听器:

var el = document.getElementById('myButton')
el.addEventListener( 'click', function(){
    alert('Hello!');
}, false)
targetElement.addEventlistener(typeOfEvent,listenerFunction,useCapture);
W3C DOM Leaval 2通用语法

尽管这个方法看起来比之前那个方法复杂了一点,但是那些额外的代码还是有必要打出来的。对于DOM级别2的事件最大的好处就是一个事件可以注册许多处理器。

addEventlistener的头两个参数是目标对象和事件,不仅如此,函数最后一个参数,可以指定处理器是在捕获阶段还是冒泡阶段被触发(通过设置”addEventListener()”函数的第三个参数来指定 – true表示在捕获阶段,false表示在冒泡阶段)

接下来将针对W3C DOM Leavl 2事件进行讨论。

事件在文档中被传递的两种模型

W3C DOM Leavl 2事件流包括三个阶段:事件捕获阶段、处于目标简短和事件冒泡阶段。

首先我们了解下事件冒泡和事件捕获。

事件冒泡

在页面上有多个事件,也可以多个元素响应同一个事件。假设网页上有两个元素,其中一个元素嵌套在另一个元素中,并且都被绑定了 click 事件,同时 body 也绑定了 click 事件,如下:

js代码如下:

window.onload = function(){ var oBubble = document.getElementById("oBubble"); var oBubble1 = document.getElementById("oBubble1"); var oBubble2 = document.getElementById("oBubble2"); oBubble.onclick = function(){ alert("Bubble") } oBubble1.onclick = function(){ alert("Bubble1") } oBubble2.onclick = function(){ alert("Bubble2") } }

html代码如下:

<body id="oBubble">//点击#oBubble弹出Bubble
    <div id="oBubble1">//点击#oBubble1先后弹出Bubble1、Bubble
        <span id="oBubble2">oBubble</span>//点击#oBubble2先后弹出Bubble2 、Bubble1、Bubble
    </div>
</body>

事件顺序:span→div→body,如下图所示:

JavaScript欲速则不达——基本处理事件详解和阻止事件传播
事件冒泡实例图例

很明显,每个元素都会按照(inside→outside)的冒泡型事件,所以事件冒泡会引起预料之外的效果。事件冒泡是IE-DOM处理事件对象的方法。

事件捕获

事件捕获和事件冒泡是刚好相反的两个过程,事件捕获是按照(outside→inside)的冒泡型事件开始触发。因此拿以上冒泡例子,alert信息是跟事件冒泡完全相反的顺序。

一些注意事项:

  1. W3C DOM Leavl 2标准的addEventListener方法执行事件的顺序是按照事件注册的顺序执行的。而IE的attachEvent方法则相反–后注册的事件先觖发,先注册的事件后触发。
  2. W3C DOM Leavl 2标准的浏览器文本节点也会冒泡,而IE内核的浏览器文本节点不会冒泡。
  3. W3C DOM Leavl 2浏览器事件对象与IE内核的浏览器事件不同(具体请参阅)。
  4. DOM标准的浏览器事件卸载方式与IE内核的事件卸载方式不同。

接下来我们就来解决跨浏览器的事件处理的方案。

跨浏览器的事件处理函数

前面我们介绍了事件冒泡和事件捕获两种事件事件获取方式,而W3C模型 是两者中和。就是事件发生时,先从顶层开始进行事件捕获,直到事件触发到达了事件源元素。然后,再从事件源往上进行事件冒泡,直到到达document。

利用W3C DOM Leavl 2事件监听器,就是 addEventListener函数。我们可以自己选择绑定事件时采用事件捕获还是事件冒泡,方法就是绑定事件时通过addEventListener函数,上面我们介绍过它的三个参数了:如果第三个参数若是true,则表示采用事件捕获,若是false,则表示采用事件冒泡。

但是在一个支持W3C DOM的浏览器中,按照DOM Leavl 1绑定事件方式,采用的全都是事件冒泡方式。大多数时候,我们也是希望事件从内部嵌套的的元素冒泡到外围元素。

解决方案

以上我们介绍过 W3C DOM Leaval 2事件绑定中的addEventlistener,可以为元素添加多个事件,而且最后一个参数还支持事件冒泡或捕获,IE6/7/8仍然没有遵循标准而使用了自己专有的attachEvent,且不支持事件捕获,所有事件都是发生在冒泡阶段。

所以创建一个可重用。实现了DOM Leavl 2事件处理的事件处理函数,但是,它还是要跨浏览器。如下经典代码

listenEvent函数代码

function listenEvent(eventTarget, eventType, evrntHandler) {
    if (eventTarget.addEventListener){
        eventTarget.addEventListener(eventType, evrntHandler, true);//IE9等其他现代浏览器
    }
    else if (eventTarget.attachEvent){
        eventType = "on"+eventType;
        eventTarget.attachEvent(eventType,evrntHandler) //IE6、7、8
    }
    else {eventTarget["on" + eventType] = evrntHandler;}//IE5~ 个人觉得不写也罢。
}

listenEvent函数使用

listenEvent(document,"click",processClick)
可重用的事件处理函数

此处理函数接受3个函数:目标对象、事件(作为一个字符串),以及函数名称。首先测试对象,看看它是否支持addEventListener(W3C DOM Leaval 2的事件监听方法),如果支持这个方法,就把事件映射到事件处理函数。回到代码,因为IE6、7、8不支持addEventListener,所以检查是否支持attachEvent,记得前面加”on“,因为不加”on“只是事件的名字,但是因为IE6、7、8只支持向上冒泡,所以此方案中 addEventListener的第三个参数是false。最后为了兼容DOM leaval 0事件处理,还要加最后一行代码。

使用W3C DOM Leavl 2处理事件事件监听不会有覆盖之前绑定事件的现象,每个绑定的事件都会被执行,不过 attachEvent 为元素增加的一系列事件不是以添加它们顺序执行的,而是以相反的顺序触发。最重要的是,采用事件监听给对象绑定方法后,可以解除相应的绑定。跟以上代码类似,removeEventListener跟addEventListener对应,detachEvent跟eventType对应,得到如下解决方案:

stopListening函数代码

function stopListening(eventTarget, eventType, evrntHandler) {
    if (eventTarget.removeEventListener){
        eventTarget.removeEventListener(eventType, evrntHandler, true);//IE9等其他现代浏览器
    }
    else if (eventTarget.attachEvent){
        detachEvent = "on"+eventType;
        eventTarget.detachEvent(eventType,evrntHandler) //IE6、7、8
    }
    else {eventTarget["on" + eventType] = null;}//IE5~
}
stopListening(document,"click",processClick)
可重用的事件处理函数

在DOM标准的事件卸载方式中需要注意的是:事件捕获的参数。如果你的事件是注册在捕获阶段,则卸载事件时,必须将其指定为捕获阶段(true),否则无法卸载;如果你的事件注册在注册在冒泡阶段,则必须将其指定为冒泡阶段(false),否则同样无法卸载。

现在,如果我们想停止监听一个事件,可以直接调用stopListening,同样传入3个参数:目标对象、事件和事件处理函数。

阻止冒泡

因为大部分浏览器都是按照DOM Leavl 1绑定事件方式,采用的全都是事件冒泡方式。所以阻止事件冒泡(嵌套元素中传播)是很有必要的。如下方案:

阻止冒泡方案:

function cancelPropagation (event){
    event = window.event||event;
    if( document.all){
        event.cancelBubble = true;
    }else{
        event.stopPropagation();
    }
}

stopListening函数使用

//这里我们采用上面介绍过的事件处理函数 listenEvent 函数
listenEvent(document.getElementById("oBubble2"),"click",function(evt){
    cancelPropagation(evt);
})
阻止冒泡

因为IE8、7、6不支持W3C DOM Leavel 2不支持,那么我们就设置event.cancelBubble 的属性值为 true;而支持其他现代浏览器则使用W3C DOM Leavel 2的,则调用stopPropagation方法。

最后罗列文章中出现的几个Event方法

  1. W3C DOM Leavl 2绑定和解除事件的方法:addEventListener和removeEventListener:
  2. IE绑定和解除事件的方法:attachEvent和detachEvent
  3. 取消事件:W3C DOM Leavl 2使用preventDefault(),iE直接返回false
  4. 阻止事件在嵌套函数中传播(阻止冒泡和捕获):W3C DOM Leavl 2使用stopPropagation(),IE中cancelBubble返回true

本文出现所有DEMO下载:js-attachEvent.rar

本文源链接:http://www.html5jscss.com/js-attach-event.html

转载请注明《JavaScript欲速则不达——基本处理事件详解和阻止事件传播》| html5jscss

评论关闭了.