资讯专栏INFORMATION COLUMN

前端小知识--JavaScript事件流

Taste / 250人阅读

摘要:事件事件指可以被侦测到的行为。事件通常与函数配合使用,当事件发生时函数才会执行。两家公司对于事件流出现了截然相反的定义。级事件规定的事件流包括三个阶段事件捕获阶段处于目标阶段事件冒泡阶段。我们又把事件处理程序称为事件侦听器。

JavaScript事件流 0.DOM级别与DOM事件
首先在介绍DOM事件之前我们先来认识下DOM的不同级别。针对不同级别的DOM,我们的DOM事件处理方式也是不一样的。
DOM级别一共可以分为4个级别:DOM0级,DOM1级,DOM2级和DOM3级,
而DOM事件分为3个级别:DOM0级事件处理,DOM2级事件处理和DOM3级事件处理。

如下图所示:

其中1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。

1.事件
事件指可以被 JavaScript 侦测到的行为。即鼠标点击、页面或图像载入、鼠标悬浮于页面的某个热点之上、在表单中选取输入框、确认表单、键盘按键等操作。事件通常与函数配合使用,当事件发生时函数才会执行。
事件名称:click/mouseover/blur("不带on")

响应某个事件的函数就是事件处理程序(事件侦听器)。
事件处理程序函数名称:onclick/onmouseove/onblur

例子代码--点击事件触发alert函数

更多事件类别请参考w3c中关于事件的详细类别。
JavaScript 事件
JavaScript 事件参考手册

2.事件流
事件流指从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。

一点背景:
早期的IE事件传播方向为由上至下,即从document逐级向下传播到目标元素;
而Netscape公司的Netscape Navigator则是朝相反的方向传播,也就是从目标元素开始向上逐级传播最终至window。 两家公司对于事件流出现了截然相反的定义。

后来ECMAScript在DOM2中对事件流进行了进一步规范,基本上就是上述二者的结合。
当事件发生时,最先得到通知的是window,然后是document,由上至下逐级依次而入,直到真正触发事件的那个元素(目标元素)为止,这个过程就是捕获。
接下来,事件会从目标元素开始起泡,由下至上逐级依次传播,直到window对象为止,这个过程就是冒泡。
所以捕获比冒泡先执行。
其中DOM3级事件在DOM2的基础之上添加了更多的事件类型。

DOM2级事件规定的事件流包括三个阶段:
(1)事件捕获阶段(2)处于目标阶段(3)事件冒泡阶段。
下面图片来自:https://www.w3.org/TR/DOM-Lev...

我们写一个例子:如下图,中间白色区域的盒子分别为box1,box2...box6,包含控制按钮设置我们的事件

    

点击按钮设置类型后再点击中心

点击

大概流程图如下:

演示效果如图:

例子源码
参考链接————小侠同学

3.事件处理程序
前面我们已经说到了,事件处理程序就是响应某个事件的函数,简单地来说,就是函数。我们又把事件处理程序称为事件侦听器。事件处理程序是以"on"开头的,比如点击事件的处理程序是"onclick",事件处理程序大概有以下5种。

1.HTML事件处理程序

2.DOM0级事件处理程序

3.DOM2级事件处理程序

4.IE事件处理程序

5.跨浏览器的事件处理程序

3.1 HTML事件处理程序

像我们的第一个例子,就是HTML事件处理程序,它是写在html里面的,是全局作用域:

例子代码--点击事件触发alert函数

当我们需要使用一个复杂的函数时,将js代码写在这里面,显然很不合适,所以有了下面这种写法:

例子代码--点击事件触发doSomething()函数,这个函数写在多带带的js文件或

可以看到button.onclick这种形式,这里事件处理程序作为了btn对象的方法,是局部作用域。
所以我们可以用

btn.onclick = null;来删除指定的事件处理程序。

如果我们尝试给事件添加两个事件,如:


 

输出,hello again,很明显,第一个事件函数被第二个事件函数给覆盖掉了,所以,DOM0级事件处理程序不能添加多个,也不能控制事件流到底是捕获还是冒泡。

3.3 DOM2级事件处理程序(不支持IE)

进一步规范之后,有了DOM2级事件处理程序,其中定义了两个方法:
addEventListener() ---添加事件侦听器
removeEventListener() ---删除事件侦听器
具体用法看
1.https://developer.mozilla.org...
2.https://developer.mozilla.org...
函数均有3个参数,
第一个参数是要处理的事件名(不带on前缀的才是事件名)
第二个参数是作为事件处理程序的函数
第三个参数是一个boolean值,默认false表示使用冒泡机制,true表示捕获机制。


 

这时候两个事件处理程序都能够成功触发,说明可以绑定多个事件处理程序,但是注意,如果定义了一摸一样时监听方法,是会发生覆盖的,即同样的事件和事件流机制下相同方法只会触发一次,比如:


 

removeEventListener()的方法几乎和添加时用法一摸一样:


 

这样的话,事件处理程序只会执行一次。
但是要注意,如果同一个监听事件分别为“事件捕获”和“事件冒泡”注册了一次,一共两次,这两次事件需要分别移除。两者不会互相干扰。
这时候的this指向该元素的引用。
这里事件触发的顺序是添加的顺序。

3.4 IE事件处理程序

对于 Internet Explorer 来说,在IE 9之前,你必须使用 attachEvent 而不是使用标准方法 addEventListener。
IE事件处理程序中有类似与DOM2级事件处理程序的两个方法:
1.attachEvent()
2.detachEvent()
它们都接收两个参数:
1.事件处理程序名称。如onclick、onmouseover,注意:这里不是事件,而是事件处理程序的名称,所以有on。
2.事件处理程序函数。
之所以没有和DOM2级事件处理程序中类似的第三个参数,是因为IE8及更早版本只支持冒泡事件流。
removeEventListener()的方法几乎和添加时用法一摸一样:


 

这里事件触发的顺序不是添加的顺序而是添加顺序的相反顺序。
使用 attachEvent 方法有个缺点,this 的值会变成 window 对象的引用而不是触发事件的元素。

3.5 跨浏览器的事件处理程序
为了兼容IE浏览器和标准的浏览器,我们需要编写通用的方法来处理:
var EventUtil = {
    addHandler: function (element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function (element, type, handler) {
        if (element.removeEventListener()) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    }
};

这一部分需要创建两个方法:
addHandler() --这个方法职责是视情况来使用DOM0级、DOM2级、IE事件处理程序来添加事件。
removeHandler()--这个方法就是移除使用addHandler添加的事件。
这两个方法接收相同的三个参数:
1.要操作的元素--通过dom方法获取
2.事件名称--注意:没有on,如"click"、"mouseover"
3.事件处理程序函数--对应的函数

使用:


 
4.事件对象
事件对象是用来记录一些事件发生时的相关信息的对象,但事件对象只有事件发生时才会产生,并且只能是事件处理函数内部访问,在所有事件处理函数运行结束后,事件对象就被销毁!

属性和方法如图,详细请查看以下链接:
1.HTML DOM Event 对象:http://www.w3school.com.cn/js...
2.详细介绍请查看:http://www.jb51.net/article/9...

4.1 属性

下面是一个例子:


 

在这个例子里,我们用到了currentTargeteventPhase 属性。

4.2 方法

Event对象主要有以下两个方法,用于处理事件的传播(冒泡、捕获)和事件的取消。
stopPropagation()——冒泡机制下,阻止事件的进一步往上冒泡

    var btn1=document.getElementById("btn1");
    var content=document.getElementById("content");
    btn1.addEventListener("click",function(event){
        alert("btn1");
        event.stopPropagation();
    },false);
    content.addEventListener("click",function(){
        alert("content");
    },false);
    //这里会输出btn1,阻止了向content的冒泡

preventDefault()——用于取消事件的默认操作,比如链接的跳转或者表单的提交,主要是用来阻止标签的默认行为

禁止跳转
var go = document.getElementById("go");
function goFn(event) {
 event.preventDefault();
// 不会跳转
}
go.addEventListener("click", goFn, false);
4.3 兼容性

当然,事件对象也存在一定的兼容性问题,在IE8及以前本版之中,通过设置属性注册事件处理程序时,调用的时候并未传递事件对象,需要通过全局对象window.event来获取。解决方法如下:

function getEvent(event) {
 event = event || window.event;
}

在IE浏览器上面是event事件是没有preventDefault()这个属性的,所以在IE上,我们需要设置的属性是returnValue

window.event.returnValue=false

stopPropagation()也是,所以需要设置cancelBubble,cancelBubble是IE事件对象的一个属性,设置这个属性为true能阻止事件进一步传播。

event.cancelBubble=true
5.事件委托
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

例子说明,我们为ul添加新的li,其中对li标签元素绑定了click事件,但是发现,后增加的元素没有办法触发我们的click事件。

    
    
  • 1
  • 2
  • 3

这是因为如果事件涉及到更新HTML节点或者添加HTML节点时,新添加的节点无法绑定事件,更新的节点也是无法绑定事件,表现的行为是无法触发事件。
其中一种解决方法是,添加子节点的时候,再次为其添加监听事件

    
    
  • 1
  • 2
  • 3

这也是问题所在:
 1.首先我们多次操作DOM获取元素,这样势必会降低浏览器处理性能
 2.事件不具有继承性,如果我们动态在页面中添加了一个元素,那么还需要重新走一遍上述程序为其添加监听事件

那么有没有更好的方法呢?根据事件的冒泡原理,我们还可以实现另外一个很重要的功能:事件委托

我们只监听最外层的元素,然后在事件函数中根据事件来源进行不同的事件处理。这样,我们添加事件监听时只需要操作一个元素,极大的降低了DOM访问,并且不用再给新增的元素添加监听事件了,因为元素的事件会冒泡到最外层,被我们截获。

    
    
  • 1
  • 2
  • 3

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,所以要判断点击的对象到底是不是li标签元素。

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题)。

这样,我们就实现了我们的事件委托,当然,不是所有的事件都是可以委托的。
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,新增加的节点也可以触发事件效果。

参考:
1.http://www.cnblogs.com/souven...
2.https://www.cnblogs.com/st-le...
3.https://segmentfault.com/a/11...
4.http://www.jb51.net/article/9...
5.http://www.w3school.com.cn/js...
6.http://www.jb51.net/article/8...
7.http://www.jb51.net/article/9...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107424.html

相关文章

  • 前端基础 - 事件入门

    摘要:及更早版本不支持事件流。绑定作用域其他的地方,就跟类似,都可以绑定多个事件处理程序,要删除只能调用,并且不能使用匿名函数的形式。 事件知识快速入门 事件是前端开发必备的知识,我通过阅读《JavaScript高级程序设计》,梳理了一下整个事件的知识体系,下面一起来学习吧。 1. 背景知识 跟所有开发UI的思路一样,JavaScript与HTML之间通过事件来进行交互。事件,就是文档或浏览...

    bbbbbb 评论0 收藏0
  • 认识node核心模块--从Buffer、Stream到fs

    摘要:端输入数据到端,对就是输入流,得到的对象就是可读流对就是输出端得到的对象是可写流。在中,这四种流都是的实例,它们都有事件,可读流具有监听数据到来的事件等,可写流则具有监听数据已传给低层系统的事件等,和都同时实现了和的事件和接口。 原文地址在我的博客 node中的Buffer和Stream会给刚接触Node的前端工程师们带来困惑,原因是前端并没有类似概念(or 有我们也没意识到)。然而,...

    TANKING 评论0 收藏0
  • 【连载】前端个人文章整理-从基础到入门

    摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...

    madthumb 评论0 收藏0
  • 前端事件绑定知识点(面试常考)

    摘要:事件通常与函数配合使用,当事件发生时函数才会执行。的事件流是事件捕获流,事件由根元素获取并沿树向下分发。通过添加事件,只能用删除此事件。这主要得益于浏览器的事件冒泡机制。 简介 事件是可以被 JavaScript 侦测到的行为。 网页中的每个元素都可以产生某些可以触发 JavaScript 函数或程序的事件。 事件通常与函数配合使用,当事件发生时函数才会执行。 执行JS 事件的方式: ...

    liujs 评论0 收藏0

发表评论

0条评论

Taste

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<