浏览器中关于事件的那点事儿

作者: 顽Shi  发布时间: 2014-02-01 20:22  阅读: 7830 次  推荐: 25   原文链接   [收藏]  
摘要:事件在Web前端领域有很重要的地位,很多重要的知识点都与事件有关。本文旨在对常用的事件相关知识做一个汇总和记录。

  在前端中,有一个很重要的概念就是事件。我对于事件的理解就是使用者对浏览器进行的一个动作,或者说一个操作。

  本文会介绍很多与事件有关的东西,虽然我的出发点有那么点一网打尽的意思m不过也难以盖全,所以就把最常用,最基本也相对重要的内容拿出来记录一下。

  Javascript绑定事件的方式

  传统的事件绑定

  因为各种历史原因,事件的绑定在不同的浏览器总是有不同的写法,当然现在可能大多数人都已经习惯于jQuery的事件绑定,而不清楚javascript的原生事件绑定是什么样子。

  非常传统的事件的绑定方式,是在一个元素上直接绑定方法,element.onclick = function(e){}

<body>
<input type="button" id="bt" name="bt button" value="this is a button">
<script>
var bt = document.getElementById("bt");
bt.onclick = function(e){
alert("this is a alert");
alert(e.currentTarget.name);
}
</script>
</body>

  这是传统的事件绑定,它非常简单而且稳定,适应不同浏览器。e表示事件,this指向当前元素。但是这样的绑定只会在事件冒泡中运行,捕获不行。一个元素一次只能绑定一个事件函数。

  W3C方式的事件绑定

  W3C中推荐使用的事件绑定是用addEventListener()函数,element.addEventListener('click',function(e){...},false),上代码:

<body>
<input type="button" id="bt" name="bt button" value="this is a button">
<script>
var bt = document.getElementById("bt");
bt.addEventListener('click',function(e){
alert("this is a alert");
alert(e.currentTarget.name);
},false);
</script>
</body>

  如此的效果和之前的传统绑定方式是一样的,这种绑定同时支持捕获和冒泡,addEventListener()函数最后的参数表达了事件处理的 阶段——false(冒泡),true(捕获)。这种方式最重要的好处就是对同一元素的同一个类型事件做绑定不会覆盖,会全部生效。比如对上面代码bt元 素在进行一次click的绑定,那么两次绑定的事件处理函数都会执行,按照代码书写顺序。

  但是IE浏览器不支持addEventListener()函数,只能在IE9以上(包括IE9)可以使用,IE浏览器相应的要使用attachEvent()函数代替。

  IE下的事件绑定

  IE下事件绑定的函数是attachEvent,它支持全系列的IE,但是如果你在Chrome等其他内核浏览器中使用这个函数去绑定事件,浏览器会报错的。

<body>
<input type="button" id="bt" name="bt button" value="this is a button">
<script>
var name = "world";
var bt = document.getElementById("bt");
bt.attachEvent('onclick',function(){
alert("hello "+ this.name);
});
</script>
</body>

  attachEvent()函数支持事件捕获的冒泡阶段,同时它不会覆盖事件的绑定。但是一个缺点就是它处理函数中的this指向的是全局的window,所以上面代码弹出的结果会是"hello world"。

  冒泡和捕获

  上面的绑定事件中提到了冒泡和捕获阶段的概念,这两个概念对于理解事件是很重要的,对于它们的理解还要涉及到DOM(文档对象模型)和事件流的概念。事件流就是一个事件对象沿着特定数据结构传播的这么一个过程。

  所谓的事件对象就是Event,当一个元素上绑定的事件被触发时会产生一个事件对象,从一切皆对象的观点看这是很符合逻辑的。冒泡和捕获讲的就是事件流在DOM中两种不同的传播方式。对于冒泡和捕获的理解,我们还是从一个小的示例来看。

<body>
<div id="bt1" style="width:300px;height:300px;border:1px solid red" name="divbt1">
<div id="bt2" style="width:100px;height:100px;border:1px solid red" name="divbt2"></div>
</div>
<script>
var bt1 = document.getElementById("bt1");
bt1.onclick = function(e){
alert("bt1");
}
var bt2 = document.getElementById("bt2");
bt2.onclick = function(e){
alert("bt2");
}
</script>
</body>

  这里我们使用最简单的,最原始的事件绑定方式。2个div嵌套并且绑定有弹窗事件,那么当我们点击里面的div的时候,两个div的点击事件都会被触发这个是没有疑问的,那么它们的处理函数谁先被执行?

  这里用IE8/9/10和Chrome浏览器同时实验,结果都是先弹出bt2,然后弹出bt1,也就是里面小div的事件先被处理了。我们来思 考一下这是什么样的一个顺序,从DOM的结构上看,应该是这样的body > bt1 > bt2。我们把这个结构竖过来,bt2在整个结构的最下面,body在最上面。想象一下,当点击发生时产生一个泡泡(也就是事件对象),然后这个泡泡慢 慢,向上浮,首先路过bt2,然后路过bt1,在路过它们时依次执行事件函数,这就是冒泡型事件。

  与之相反的就是捕获型事件,它的事件流传播的顺序正好与冒泡型事件完全相反。也就是bt1上的事件先触发,然后传递到bt2。捕获是由表及里,冒泡是由内之外。

  那么现在回忆一下之前的W3C标准中那个addEventListener()函数,它里面最后一个参数false代表冒泡,true代表捕 获,这是什么意思呢?因为W3C作为一个标准,它选择了一个相对折中的方案去处理事件,也就是任何在W3C事件模型中发生的事件都先进入捕获阶段,然后在 进入冒泡阶段,保证一个事件会经历这两个阶段,以适应IE和其他非IE浏览器,这样使用者可以根据需求选择将事件注册到哪一个阶段。

  现在再来看用addEventListener()函数进行事件绑定的结果:

<body>
<div id="bt1" style="width:300px;height:300px;border:1px solid red" name="divbt1">
<div id="bt2" style="width:100px;height:100px;border:1px solid red" name="divbt2"></div>
</div>
<script>
var bt1 = document.getElementById("bt1");
bt1.addEventListener('click',function(e){
alert("bt1");
},false);
var bt2 = document.getElementById("bt2");
bt2.addEventListener('click',function(e){
alert("bt2");
},false);
</script>
</body>

  这里2个div的事件绑定类型一共有4个可能的组合:2个false;2个true;1个false,1个true;1个true,1个false。这里分别试验下吧,记住按照W3C标准,捕获阶段会在冒泡之前。

  jQuery绑定事件的方式

  上面我们记录了关于javascript原生的事件绑定的一些写法,这里我们在介绍一下通过jQuery进行事件绑定的方式。首先来夸一夸 jQuery的好,通过jQuery绑定让我们省去了考虑浏览器兼容和事件流程序的相关细节内容。jQuery中对于事件的绑定称为委托,这是一个很好的 定义,所谓委托,顾名思义就是自己不去做,我让别人帮我做这个事。jQuery就是这么做的,让我们详细了解下。

  .bind()

  我们直接看代码,bind()函数使用很简单。

<body>
<div id="div1" style="width:300px;height:300px;border:1px solid red" name="divbt1">
<script>
$("#div1").bind('click',function(e){
alert("div1 " + e.currentTarget.name);
});
</script>
</body>

  代码在IE8, IE11, Chrome运行都没有问题,我们简单翻译一下就是首先找到id为div1的div对象,然后给这个对象绑定一个 click事件。现在来分析一下bind(),首先,如果用它绑定事件要有一个寻找jQuery对象的过程;其次,如果要为大量的元素绑定事件那么要寻找 大量的对象不说,每一个对象还要占用内存来存储相应的处理函数。并且bind()只能为当前已存在的DOM节点绑定事件,如果节点还没有产生bind是没 有办法的。

  所以说bind()推荐在使用比较简单的情况中,绑定不多的节点并且没有新节点产生的情况,如果比较复杂就推荐使用delegate()。

  .delegate()

  在jQuery中还有一个live()函数也能处理类似的问题,但是不如delegate()好用,所以这里就不介绍了。delegate()是为了突破单一bind()方法的局限性,实现事件的委托。我们先看代码来理解:

<body>
<div id="div1" style="width:300px;height:300px;border:1px solid red" name="divbt1">
<div id="div2" style="width:100px;height:100px;border:1px solid red" name="divbt2"></div>
</div>
<script>
$("body").delegate('#div1','click',function(e){
alert("div1");
});
$("body").delegate('#div2','click',function(e){
alert("div2");
});
</script>
</body>

  解 读一下delegate()函数,我们寻找到body标签的对象并调用delegate(),这是把事件的执行委托给body。也就是监听整个DOM树, 当触发事件的DOM节点的是id为div1,触发触发事件的类型是click时,在事件传播到body时,我们执行相应的处理函数。body怎么能知道这 么多,它如何知道绑定在它身上的执行函数什么时候执行?jQuery这些事件委托的原理根据事件冒泡的机制,广播的时候所有的节点都会知道,到底发生了什 么。

  DOM在为页面中的每个元素分派事件时,相应的元素一般都在事件冒泡阶段处理事件。在类似 body > div > a 这样的结构中,如果单击a元素,click事件会从a一直冒泡到div和body(即document对象)。因此,发生在a上面的单击事件,div和 body元素同样可以处理。而利用事件传播(这里是冒泡)这个机制,就可以实现事件委托。具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务 委托给其父元素或者祖先元素,甚至根元素(document)。

  事件的取消

  在一些情况下我们需要阻止事件流的传播,或者解除之前绑定的事件。在实际工作中经常会遇到类似的需求,尤其是事件流的阻止。

  事件流阻止

  某些事件的对象是可以取消的,这意味着可以阻止默认动作的发生。事件对象是否可以取消,要通过Event.cancelable属性表示。事件 监听器可以调用Event.preventDefault()取消事件对象的默认动作。Event.stopPropagation()方法可以阻止事件 向上冒泡。

  事件的阻止根据场景不同和浏览器不同有不同的处理,因为事件处理模型不同的关系。如果在IE下,Event.returnValue = false就可以;如果是非IE下,用Event.preventDefault()阻止。事件流阻止,这里面阻止的是它的继续传播以及有可能产生的默认 动作。这里举一个常见且简单的例子,就是submit类型按钮的点击。

<body>
<form action="asd.action">
<input type="submit" id="tijiao" value="submit"/>
</form>
<script>
$("body").delegate('#tijiao','click',function(e){
e.preventDefault();
});
</script>
</body>

  这里点击按钮,form表单默认的提交被阻止了,也就是其默认动作终止了。这里有一个强调的就是滚动事件,滚动也是经常遇到需要处理的事件类型,但是滚动的阻止有点特例,它不支持在委托里进行阻止。

  说到这里我们感觉Event.preventDefault()和Event.stopPropagation()都可以阻止事件,那么它们有什么区别?

  前者是通知浏览器不要执行与事件相关联的默认动作,比如submit类型的按钮点击会提交;后者是停止事件流的继续冒泡,但是它对IE8及以下IE浏览器支持不好。如果直接使用return false则表示终止处理函数。

  事件函数的解除绑定

  和事件的绑定其实是相对应的,如果需要接触事件的绑定,运行对应的函数就可以了。如果是原生JS绑定则对应运行removeEventListener()和detachEvent()。看一个代码示例:

    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;
}
}
};

  如果是jQuery的绑定,也是存在对应的解绑函数用以清除注册事件,比如unbind()和undelegate()。

转大神

Browser的更多相关文章

  1. HTML BOM Browser对象

    BOM:Browser Object Model,即浏览器对象模型,提供了独立于内容的.可以与浏览器窗口进行互动的对象结构. Browser对象:指BOM提供的多个对象,包括:Window.Navig ...

  2. 模型浏览器【Model Browser】【EF基础系列6】

    We have created our first Entity Data Model for School database in the previous section. The visual ...

  3. SQLite文件查看工具DB Browser for SQLite

    有时候,我们用Python创建了一个test.sqlite文件,想查看里面的数据,除了用Python连上数据库,SELECT出来,还有什么好办法呢?这里推荐使用一个小工具DB Browser for ...

  4. Selenium通过WebDriver控制IE浏览器出错 Browser zoom level was set to 109%. It should be set to 100%

    错误信息: WebDriverException: Message: Unexpected error launching Internet Explorer. Browser zoom level ...

  5. js get browser vertion (js获取浏览器信息版本)

    1问题:js get browser vertion (js获取浏览器信息版本) 2解决方案 Copy this script into your JavaScript files. It works ...

  6. linux(debian) arm-linux-g++ v4.5.1交叉编译 embedded arm 版本的QtWebkit (browser) 使用qt 4.8.6 版本

    最近需要做一个项目 在arm 架构的linux下 没有桌面环境的情况下拉起 有界面的浏览器使用. 考虑用qt 的界面和 qtwebikt 的库去实现这一系列操作. 本文参考: Qt移植到ARM Lin ...

  7. SQL Server : Browser服务

    SQL Server : Browser服务是SQL Server 2005新增的,简单的说,如果一个物理服务器上面有多个SQL Server实例,那么为了确保客户端能访问到正确的实例,所以SQL S ...

  8. VS2013的 Browser Link 引起的问题

    环境:vs2013 问题:在调用一个WebApi的时候出现了错误: 于是我用Fiddler 4直接调用这个WebApi,状态码是200(正常的),JSon里却提示在位置9409处文本非法, 以Text ...

  9. Visual Studio 2013中因SignalR的Browser Link引起的Javascript错误一则

    众所周知Visual Studio 2013中有一个由SignalR机制实现的Browser Link功能,意思是开发人员可以同时使用多个浏览器进行调试,当按下IDE中的Browser Link按钮后 ...

  10. 关于PB调用Microsoft Web Browser控件的一些问题

    Microsoft Web Browser控件是WINDOWS系统自带的控件,一般不需要单独安装,由于工作的需要,把使用中遇到的问题记录一下,以便查阅. 插入控件: 环境为PB12.0,insert- ...

随机推荐

  1. Spring IOC容器的初始化-(三)BeanDefinition的注册

    ---恢复内容开始--- 前言 在上一篇中有一处代码是BeanDefiniton注册的入口,我们回顾一下. 1.BeanDefiniton在IOC容器注册 首先我们回顾两点,1. 发起注册的地方:2. ...

  2. Collections排序

    0.前言 ThreeSet的底层实现是红黑树,它在创建set的过程中实现排序.Collections.sort是在对整个集合进行排序,按道理来说使用TreeSet插入集合元素直至建立整个TreeSet ...

  3. Hibernate问题集锦: 概述

    Hibernate问题集锦: 概述   ImportNew注: 本文是ImportNew编译整理的Java面试题系列文章之一.你可以从这里查看全部的Java面试系列. Q.怎么配置hibernate? ...

  4. 在rebar发布的项目中添加监视工具

    默认使用rebar创建的项目没法使用observer,可以如下操作 修改app.src {application, tcp_server, [ {description, ""}, ...

  5. AngularJS:教程

    ylbtech-AngularJS:教程 1.返回顶部 1. AngularJS 教程 AngularJS 通过新的属性和表达式扩展了 HTML. AngularJS 可以构建一个单一页面应用程序(S ...

  6. C# 异常内存信息导出Dump文件

    背景:很多情况下程序崩溃我们只能看到程序抛出来的异常信息,但是有时候异常信息不清不楚我们处理异常还是一头雾水,这种情况下我们就很希望能有种办法获取程序运行时的内存进行调试,查看其中的变量.参数.方法执 ...

  7. mysql字符类型

    字符类型 #官网:https://dev.mysql.com/doc/refman/5.7/en/char.html #注意:char和varchar括号内的参数指的都是字符的长度 #char类型:定 ...

  8. 【linux】下载命令lftp\sftp\wget

    sftp sftp(安全文件传输协议)与ftp 有着几乎一样的语法和功能.FTP 为 SSH的一部份,是一种传输档案至 Blogger 伺服器的安全方式.它并不使用ftp守护进程(ftpd或wu-ft ...

  9. vs2010一运行就报错deven.exe assert failure 解决方法,卸载系统中.netFramework最新版本的(简体中文)

    vs2010一运行就报错deven.exe assert failure 解决方法,卸载系统中.netFramework最新版本的(简体中文)

  10. 什么是DA控制

    液压系统中的DA控制: Automotive Drive control & Anti stall control 自动变速控制 & 防熄火控制 (DA控制) 1:自动(变速)驱动控制 ...