Skip to content

事件 ​

本节目标:理解“事件”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 JavaScript与HTML之间的交互是通过==事件==实现的。

事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。 可以使用侦听器(或者处理程序)来预订事件,以便事件发生时执行相应的代码。 这种在传统软件工程中被称为观察员模式的模型,支持页面的行为(Javascript代码)与页面外观(HTML和CSS代码)之间的松散耦合。

==事件是元素天生自带的行为,我们操作元素,就会触发相关的事件行为。==

==事件==最早是在IE3和Netscape Navigator 2中出现的,当时作为分担服务器运算负载的一种手段。在IE4和Navigator 4发布时,这两种浏览器都提供了==相似但不相同==的API,这些API并存经过了好几个主要版本。

DOM2级规范开始尝试以一种符合逻辑的方式来==标准化==DOM事件。IE9、Firefox、Opera、Safari和Chrome全都已经实现了“DOM2级事件”模块的==核心==部分。 IE8是最后一个仍然使用其专有事件系统的主要浏览器。

浏览器的事件系统相对比较复杂。尽管所有主要浏览器已经实现了“DOM2级事件”,但这个规范本身并没有涵盖所有事件类型。浏览器对象模型BOM也支持一些事件,这些事件与文档对象模型DOM事件之间的关系并不十分清晰,因为BOM事件长期没有规范可以遵循(HTML5后来给出了详细的说明)。随着DOM3级的出现,增强后的DOM事件API变得更加繁琐。使用事件有时相对简单,有时则非常复杂,难易程度会因你的需求而不同。不过,有关事件的一些核心概念是一定要理解的。

事件流 当浏览器发展到第四代时(IE4及Netscape Communicator 4),浏览器发展团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上所有的圆。两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。

==事件流==描述的是从页面中接收事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了差不多完全相反的事件流概念。 IE的事件流是==事件冒泡流==; 而Netscape Communicator 的事件流是==事件捕获流==。

【事件冒泡】IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的HTML页面为例:

<!DOCTYPEhtml>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>

如果你单击了页面中的\<div\>元素,那么这个click事件会按照如下顺序传播: 1. \<div\>2. \<body\>3. \<html\>4. \<document\>

也就是说,click事件首先在\<div\>元素上发生,而这个元素就是我们单击的元素。然后,click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。 所有现代浏览器都支持事件冒泡。IE9、Firefox、Chrome、和Safari则将事件一直冒泡到window对象。

【事件捕获】Netscape Communicator团队提出的是另一种事件流叫做事件捕获(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收事件。事件捕获的用意在于在事件到达预定目标之前捕获它。如果仍以前面的HTML页面作为演示事件捕获的例子,那么单击\<div\>元素就会以下列顺序触发click事件。 1.document2.\<html\>3.\<body\>4.\<div\>

在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树依次向下,一直传播到事件的实际目标,即\<div\>元素。 虽然事件捕获是Netscape Communicator 唯一支持的事件流模型,但IE9、Safari、Chrome、Opera和Firefox目前也支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件。

==由于老版本的浏览器不支持,因此很少有人使用事件捕获。我们也建议读者放心地使用事件冒泡,在有特殊需要时再使用事件捕获。==

DOM事件流

DOM2

级事件 规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。以前面简单的HTML页面为例,单击\<div\>元素会按下图顺序触发事件。

在DOM**事件流中,**实际 的目标(\<div\>元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到\<html\>再到\<body\>后就停止了。下一个阶段“处于目标”阶段,于是事件在\<div\>上发生了,并在事件处理中被看成冒泡阶段的一部分。然后冒泡阶段发生,事件又传播加文档。

多数DOM事件流的浏览器都实现了一种特定的行为;即使“

DOM2

级事件”规范明确要求捕获阶段不会涉及事件目标,但IE9 Safari Chrome Firefox和

Opera9.5

及更高版本都会在捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上面操作事件。

事件处理程序 事件就是用户或浏览器自身执行的某种动作。如

click load

和mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以on开头,因此click事件的事件处理程序就是onclick。

为事件指定处理程序的方式有好几种 HTML事件处理程序 某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值已于一九能够执行的Javascript代码。 //按钮单击时执行一些

Javscript
<input type="button" onclick="alert('Clicked')" />

这个操作是通过指定onclick特性,并将一些Javascript代码作为它的值来定义的。由于这个值是Javascript,因此不能在其中使用未经转义的HTML语法字符,例如和号&,双引号“”,小于号\<,大于号\>。为了避免使用HTML实体,这里使用了单引号。如果想使用双引号,那么就要将代码改写成 \<input type="button" onclick="alert(&quot;Clicked&quot;)" /\>

另外,在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其它地方定义的脚本,如下例:

<script type="text/javascript">
function showMessage() {
        alert("Hello world!");
}
</script>
<input type="button" onclick="showMessage()" />

在这个例子中,单击按钮就会调用showMessage()函数。这个函数是在一个独立的\<script\>元素中定义的。当然也可以被包含在一个外部文件中。事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。

这样指定的事件处理程序具有一些独到之处。首先,这样会创建一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象

<input type="button" onclick="alert('event.type')" />

通过event变量,可以直接访问事件对象,你不用自己定义它,也不用从函数的参数列表中读取。 在这个函数内部,this值等于事件的目标元素,例如:

<input type="button" value="Click Me"onclick="alert('this.value')" />

关于这个动态创建的函数,另一个有意思的地方是它扩展作域的方式。在这个函数内部,可以像访问局部变量一样访问document及该元素本身的成员。这个函数使用with像下面这样扩展作用域:

function() {
     with(document){
            with(this) {
                   //

元素属性值

          }
    }
}

这样,事件处理程序要访问自己的属性就简单多了。下面代码与前面例子效果相同 \<input type="button" value="Click Me"onclick="alert('value')" /\>

如果当前元素是一个表单输入元素,则作用域中还会包含访问表单元素(父元素)的入口,这个函数就变成如下所示:

function() {
     with(document){
            with(this.form) {
            with(this) {
                   //

元素属性值

          }
          }
    }
}

实际上,这样扩展作用域的方式,无非就是想让事件处理程序无需引用表单元素就能访问其它表单字段,例如

<form method="post">
<input type="text" name="username" value="" >
<input type="button" value="Echo Username" onclick="alert(username.value)" >
</form>
//

单击按钮会显示文本框中的内容,这里直接引用了username元素。

在HTML中指定事件处理程序有两个缺点。 首先,存在一个时差问题,因为用户可能会在HTML元素一出现在页面上就触发相应事件,但当时事件处理程序有可能尚不具备执行条件。以前面的例子说明,假设showMessage()函数是在按钮下方,页面最底部定义的。如果用户在页面解析showMessage()函数前就单击了按钮,就会引发错误。为此,很多HTML事件处理程序都会被封装在一个try-catch块中,以便错误不会浮出水面,如:

<input type="button" onclick="try{showMessage();}catch(ex){}" />

这样用户将会看不到Javascript错误,因为在浏览器有机会处理错误之前,错误就被捕获了。

别一个缺点是,这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同Javascript引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。

还有一个缺点是HTML与Javascript代码紧密耦合。如果要更换事件处理程序,就要改两个地方:HTML代码和Javascript代码。而这正是许多开发人员摒弃HTML事件处理程序,转而使用Javascript指定事件处理程序的原因所在。

DOM0

级事件处理程序 通过Javascript指定事件处理程序的传统方式,就是将一个函数赋值给元素的事件处理程序属性。它有两大优势,一是简单,二是具有跨浏览器的优势。要使用Javascript指定事件处理程序,

首先必须取得一个要操作的对象的引用。 每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写,将这种属性值设置为一个函数,就可以指定事件处理程序;

使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候事件处理程序是在元素的作用域中运行;换句话说,程序中的this引用当前元素。举个例子。 var btn = document.getElementById('myBtn'); btn.onclick = function() { alert("Clicked"); }

要注意的是,在这些代码运行之前不会指定事件处理程序,因此如果这些代码在页面中位于按钮后面,就有可能在一段时间内怎么单击都没有反应。 var btn = document.getElementById('myBtn'); btn.onclick = function() { alert("this.id");//myBtn }; 单击按钮显示的是元素的ID,这个ID是通过this.id取得的。不仅是ID,实际上可以在事件处理程序中通过this访问元素的任何属性和方法。以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

也可以删除通过DOM0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null即可: btn.onclick = null;//删除事件处理程序

DOM2****级事件处理程序 DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()和removeEventListener() 所有DOM节点中都包含这两个方法,并且它们都接受三个参数: 要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果为false,表示在冒泡阶段调用事件处理程序。

要在按钮上为click事件添加事件处理程序,可以使用下列代码: var btn = document.getElementById('myBtn'); btn.addEventListener("click", function() { alert(this.id); }, false);

【旧版本IE中使用的事件捕捉方式】

  • setCapture()
  • releaseCapture() ``它们适用于处理鼠标类事件,对于其他类型的事件(例如键盘类事件)则不起作用。

[阻断传播] 如何让事件停止它的冒泡式传播。

//==现有的标签==,==然后再定义一个用来处理该段落单击事件的函数:==

<p id="closer">final<p>
function paraHandler(){
alert('clicked paragraph');
}
//

==现在,我们将该函数设置为单击事件的监听器:==

var para = document.getElementById('closer');
para.addEventListenera('click',paraHander,false);
//

==同时,我们还可以将单击监听器设置给==body, document==乃至整个浏览器的==window==对象:==

document.body.addEventListener('click',function(){
alert('clicked body');
},false);
document.addEventListener('click',function(){
alert('clicked doc');
});
windwo.addEventListener('click',function(){
alert('clicked windwo');
});

需要注意的是,按照DOM标准来说,window事件是不存在的。这就是为什么DOM指的是文档而不是浏览器。因此,实际上浏览器对于window事件的实现与DOM事件的实现并不一致。 现在,如果我们单击一下该段落,就会看到四个警告窗,它们分别是:

clicked paragraph;
clicked body;
clicked doc;
clicked window

这诠释了同一单击事件从具体标签向整个窗口传播的全过程(也就是向上冒泡的全过程)。 addEventListener()方法的对立面就是removeEventListener(),该方法的参数与前者相同。下面我们移除该段落上的监听器。 para.removeEventListener('click',paraHandler,false); 现在如果再次单击段落,就只会弹出body, document对象及window对象的单击事件窗了。 下面,我们来阻断事件的传播。首先要定义一个以事件对象为参数的函数,并在函数内对该对象调用stopPropagation()方法:

function paraHandler(e){
alert('clicked paragraph');
e.stopPropagation();
}

然后,我们添加修改后的监听器: para.addEventListener('click',paraHandler,false); 现在如果我们再单击段落,就会看到弹出窗只有一个了,因为该事件不会再上传给body, document和window了。 要提醒的是,如果我们要移除某个监听器,就必须获得之前那一个指定为监听器函数的指针。否则,即使它们的函数体完全相同也无济于事,因为两者不是同一个函数。

document.body.removeEventListener('click',function(){
alert('clicked body');
},
false);//does Not remove the handler
[

防止默认行为] 在浏览器模型中,有些事件自身就存在一些预定义行为。例如,单击链接会载入另一个页面。对此,我们可以为该链接设置监听器,并使用preventDefault()方法禁用其默认行为。 比如,我们让客户在每次单击链接之后,要回答一个问题:Are you sure you want to follow this link?。如果单击Cancel(即confirm()返回false)时,preventDefault()方法就会被调用:

//all links
var all_links = document.getElementByTagName('a');
for (var i=0;i<all_links.length; i++){
//loop all links
all_links[i].addEventListener(
'click',//event type
function(e){//handler
if(!confirm('Are you sure you want to follow this link?')){
e.preventDefault()};
},
false//don't use capturing
);
}

另外,不是所有事件的默认行为都是可以禁止的。尽管大部分事件是可以的,可以检测事件对象的cancelable属性来判断。 [跨浏览器的事件监听器] 现在绝大部分浏览器都已经完全实现了DOM Leve1标准。然后事件方面的标准化是到DOM Level2才完成的。这就导致IE9以前的版本与其它现代浏览器在这方面的实现有着不少的差异。 以下示例将会在控制台中返回被单击元素(即目标元素)的nodeName属性值: document.addEventListener('click',function(e){console.log(e.target.nodeName);},false); 由于在IE中没有addEventListener()方法,但它们从IE5开始提供了一个叫做attachEvent()的等效方法。对于更早期的版本,我们就只能通过属性方法(例如onlick属性)来解决问题了。 对于单击事件来说,使用attachEvent()就等同于使用onclick属性。 如果我们使用老式手法进行事件监听(例如,通过将某个函数赋值给onclick属性),那么当该回调函数被调用时,它不会获得相关的事件参数。但只要我们设置了事件监听器,IE中总会有一个全局对象window.event会指向该事件。 在IE的事件对象中,没有用于反映触发事件目标元素的target属性,但我们可以使用它的等效属性srcElement。 此外,IE不支持事件捕捉法,而只使用冒泡法来动作。 IE中没有stopPropagation()方法,我们可以通过将IE-only属性cancelBubble设置为true来完成相同的操作。 对于事件的取消监听操作,IE中使用的不是removeEventListener()方法,我们调用的是detachEvent()方法。 这样一来,我们就将原型的代码修改成跨浏览器版本了:

function callback(evt){
//prep work
evt=evt||window.event;
var target = evt.target || evt.srcElement;
//actual callback work
console.log(target.nodeName);
}
//start listening for click events
if (document.addEventListener){//MOdern browsers
document.addEventListner('click',callback,false);
}else if(document.attachEvent){//old IE
document.attachEvent('onclick',callback);
}else{
document.onlick = callback;//ancient
}

事件绑定:给元素天生自带的事件行为绑定方法,当事件触发,会把对应的方法执行: oBox.onclick = function(){}事件绑定的方法

  1. 内联HTML属性法
  2. 元素属性法
  3. DOM的事件监听器

内联HTML属性法 最简便也是最难维护的方式就是通过标签的特定属性来添加事件,例如: \<div onclick="alert('Ouch!')"\>click\</div\> 在这种情况下,只要该\<div\>所在的区域被用户单击了,就会触发该标签的单击事件。与此同时,其onclick属性中的字符串就会被当做JavaScript代码执行。尽管,这里并没有显式指定监听单击事件的函数,但相关环境在幕后已经为此创建了一个函数,函数的代码就等于我们为onclick属性设定的值。 元素属性法 将事件函数设置为DOM元素节点的属性。

<div id="my-div">click</div>
<script>
var myElement = document.getElementById('my-div');
myElement.onclick = function(){
alert('Ouch!');
alert('And double ouch!');
}
</script>

事件对象 在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。例如,鼠标操作导致的事件对象中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。 所有浏览器都支持event对象,但支持的方式不同。 兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会会传入event对象 键盘码 let code = ev.which || ev.keyCode; 空格键:23delete:46enter: 13ctrl:17

alt:18
#

事件类型

  1. UI事件
  2. 焦点事件
  3. 鼠标与滚轮事件
  4. 键盘与文本事件
  5. 复合事件
  6. 变动事件
  7. HTML5事件
  8. 设备事件
  9. 触摸与手势事件

【鼠标事件】 鼠标事件是Web开发中最常用的一类事件,毕竟鼠标还是最主要的定位设备。DOM3级事件中定义了9个鼠标事件,简介如下。

  • click:在用记单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着onclick事件处理程序即可以通过键盘也可以通过鼠标执行。
  • dbclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。从技术上说,这个事件并不是DOM2级事件中规定的,但鉴于它得到了广泛的支持,所以DOM3X级事件将其纳入了标准。
  • mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但DOM3级事件将它纳入了规范。IE、FireFox 9+和Opera支持这个事件。
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但DOM3级事件将它纳入了规范。IE、FireFox 9+和Opera支持这个事件。
  • mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
  • mouseout:鼠标移出,在鼠标指针位于一个元素上方,然后用户将其移入另外一个元素时触发。又移入的另一个元素可能位于前一个元素外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件。
  • mouseup:在用户释放鼠标按钮时触发。为能通过键盘触发这个事件。

【键盘事件】 用户在使用键盘时会触发键盘事件。DOM2级事件最初规定了键盘事件,但在最终定稿之前又删除了相应的内容。结果,对键盘事件的支持主要遵循的是DOM0级。 DOM3级事件为键盘事件制定了规范,IE9率先完全实现了该规范。其他浏览器也在着手实现这一标准,但委屈的很多遗留的问题。有3个键盘事件,简述如下:

  • keydown
  • keypress
  • keyup

【键盘事件】 ==事件==就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用==侦听器==(或处理程序)来预订事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为==观察员模式==的模型,支持页面的行为(JavaScript代码)与页面外观(HTML和CSS代码)之间的==松散耦合==。 周老师的分类:

  • 移动端手指事件
  • touch

[h5中的AUDIO/VIDEO音视频事件]canplay:可以播放(播放过程中可能出现资源没有加载完成

【事件类型】 不同浏览器支持的事件是不同的。 [鼠标类事件]

  • 鼠标的松开、按下、单击(按下并松开算单击一次)、双击。
  • 鼠标的悬停(指鼠标停留在某元素上方)、移出(指鼠标从某元素上方离开),拖动。

[键盘类事件]

  • 键盘键的按下、输入、松开(这三个事件是按顺序排列的)。

[载入/窗口类事件]

  • 载入(图片、页面或其他组件完成载入操作)、卸载(指用户离开当前页面)、卸载之前(由脚本提供的、允许用户终止卸载的选项)。
  • 中止(指用户在IE中停止页面或图片载入)、错误(指IE发生了JavaScript错误或图片载入失败)。
  • 调整大小(指浏览器窗口大小被重置)、滚动(指页面进行了滚动操作)、上下文菜单(即右键菜单出现)。

[表单类事件]

  • 获得焦点(指某字段获得输入)、失去焦点(指离开该字段)。
  • 改变(指改变某字段值后离开),选中(指某文本字段中的文本被选中)。
  • 重置(指擦除用户输入的所有信息)、提交(指发送表单)。

另外现代浏览器还提供拖动事件(例如dragstart, dragend, drop等)。触控设备也会有touchstart,touchmove,touchend事件等。 一张图看懂offsetX、clientX、screenX、pageX

本站总访问 — 次