《javascript高级程序设计》读书笔记(九)

最近一个多月真是忙到飞起啊,活脱脱把美少女变成了国宝,终于告一段落,留给个人,依然是无处安放的,魅力(黑眼圈)啊~
话很少说,先啃为敬 ^_^javascript


第13章 事件

JavaScript与HTML之间的交互式经过事件实现的。
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。html

事件流—事件冒泡

IE的事件流叫作事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,而后诸暨向上传播到较为不具体的节点(文档)。
好比一个<body>标签中只包含一个<div>元素的页面,若是你点击了页面中的<div>,那么这个click事件会按照以下顺序传播:
(1) <div>
(2) <body>
(3) <html>
(4) document
也就是说,click事件首先在<div>元素上发生,而这个元素就是咱们点击的元素。
而后,click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。
下图完美的展现了时间冒泡的过程。
image.pngjava

事件流—事件捕获

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
事件捕获的用意在于在事件到达预约目标以前捕获它。以上面冒泡的例子,单击<div>元素就会如下列顺序触发click事件
(1) document
(2) <html>
(3) <body>
(4) <div>
在事件捕获过程当中,document对象首先接收到click事件,而后事件沿DOM树依次向上,一直传播到事件的实际目的,即<div>元素。以下图:
image.pngios

事件流—DOM事件流

"DOM2级事件"规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。而后是实际的目标接收到事件。最后一个阶段是冒泡阶段,能够在这个阶段对事件作出响应。
image.png浏览器

事件处理程序

事件就是用户或浏览器自身执行的某种动做。诸如click、load和mouseover,都是事件的名字。而响应某个事件的函数就叫作事件处理程序(或事件监听器)。
事件处理程序的名字以"on"开头,所以click事件的事件处理程序就是onclick,load事件的事件处理程序就是onload。为事件指定处理程序的方式有好几种。框架

事件处理程序——HTML事件处理程序

事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码。
这样指定事件处理程序具备一些独到之处。首先,这样会建立一个封装着元素属性值的函数,这个函数中有一个局部变量event,也就是事件对象
<input type="button" value="Click Me" onclick="alert(event.type)"> //输出"click"
经过event变量,能够直接访问事件对象,你不用本身定义它,也不用从函数的参数列表中读取。在这个函数内部,this值等于事件的目标元素,例如:
<input type="button" value="Click Me" onclick="alert(event.type)"> //输出 "Click Me"函数

事件处理程序——DOM0级事件处理程序

每一个元素(包括window和document)都有本身的时间处理程序属性,这些属性一般所有小写,例如onclick,将这种属性的值设置为一个函数,就能够指定事件处理程序。性能

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id);    //"myBtn"
}
btn.onclick = null;      //删除事件处理程序
事件处理程序——DOM2级事件处理程序

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

var btn = document.getElmentById("myBtn");
btn.addEventListener("click",function(){
    alert(this.id);
},false);   //该事件会在冒泡阶段被触发
btn.addEventListener("click",function(){
    alert("Hello world!");
},false);

使用DOM2级方法添加事件处理程序的主要好处是能够添加多个事件处理程序。
上面的例子为按钮添加了两个事件处理程序,这两个事件处理程序会按照添加它们的顺序触发,所以首先会显示元素的ID,其次会显示"Hello world!"消息。
经过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着经过addEventListener()添加的匿名函数将没法移除。就像上面的例子同样。spa

事件处理程序——IE事件处理程序

IE实现了与DOM中相似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名字与事件处理程序函数。
要使用attachEvent()为按钮添加一个事件处理程序,可使用如下代码。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
    alert("Clicked");
});

注意,attachEvent()的第一个参数是"onclick",而非DOM的addEventListener()方法中的"click"。

在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的做用域。在使用DOM0级方法的状况下,事件处理程序会在其所属元素的做用域内运行;在使用attachEvent()方法的状况下,事件处理程序会在全局做用域中运行,因袭this等于window。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){

alert(this === window);     //true

});
在编写跨浏览器的代码时,牢记这一区别很是重要。
与addEventListener()相似,attachEvent()方法也能够用来为一个元素添加多个事件处理程序。
与DOM方法不一样的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。
使用attachEvent()添加的事件能够经过detachEvent()来移除,条件是必须提供相同的参数。与DOM方法同样,这也意味着添加的匿名函数将不能被移除。

事件对象——DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。不管指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type);       //"click"
};
btn.addEventListener("click", function(event) {
    alert(event.type);       //"click"
}, false);

event对象包含与建立它的特定事件有关的属性和方法。触发的事件类型不同,可用的属性和方法也不同。全部的事件都会有下表列出的成员。
image.png
image.png

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。
若是直接将事件处理程序制定给了目标元素,则this、currentTarget和target包含相同的值。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.currentTarget === this);     //true
    alert(event.target === this);       //true
}

要阻止特定事件的默认行为,可使用preventDefault()方法。
例如,连接的默认行为就是在被单击时会导航到其href特性指定的URL。若是你想阻止连接导航这一默认行为,那么经过连接的onclick事件处理程序能够取消它。

var link = document.getElementById("myLink");
link.onclick = function(event) {
    event.preventDefault();
}

只有cancelable属性设置为true的事件,才可使用preventDefault()来取消其默认行为。
另外,stopPropagation()方法用于当即中止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。
例如,直接添加到一个按钮的时间处理程序能够调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert("Clicked");
    event.stopPropagation();
};
document.body.onclick = function(event) {
    alert("body clicked");
}

对于上面例子,若是不调用stopPropagation(),就会在单击按钮时出现两个警告框,但是因为click事件根本不会传播到document.body,所以就不会触发注册在这个元素上的onclick事件处理程序。

事件对象的eventPhase属性,能够用来肯定事件当前正位于事件流的哪一个阶段。
若是是在捕获阶段调用的事件处理,那么eventPhase等于1;
若是事件处理程序处于目标对象上,则eventPhase等于2;
若是是在冒泡阶段调用的事件处理程序,eventPhase等于3.
这里要注意的是,尽管“处于目标”发生在冒泡阶段,但eventPhase仍然一直等于2。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase);   //2
};

document.body.addEventListener("click", function(event){
    alert(event.eventPhase);   //1
}, true);

document.body.onclick = function(event) {
    alert(event.eventPhase);   //3
};

当单击这个例子中的按钮时,首先执行的事件处理程序是在捕获阶段触发的添加在document.body中的那一个,结果会弹出一个警告框显示表示eventPhase的1。接着,会触发在按钮上注册的事件处理程序,此时的eventPhase值为2。最后一个被触发的事件处理程序,是在冒泡阶段执行的添加到document.body上的那一个,显示eventPhase的值为3。而当eventPhase等于2时,this.target和currentTarget始终都是相等的。
只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。

事件类型

Web浏览器中可能发生的事件有不少类型。

  • UI事件,当用户与页面上的元素交互时触发;
  • 焦点事件,当元素得到或失去焦点时触发;
  • 鼠标事件,当使用鼠标滚轮时触发;
  • 文本事件,当在文档中输入文本时触发;
  • 键盘事件,当用户经过键盘在页面上执行操做时触发;
  • 合成事件,当为IME输入字符时触发;
  • 变更事件,当底层DOM结构发生变化时触发。
UI事件

UI事件指的是那些不必定与用户操做有关的事件。

  • load:当页面彻底加载后在window上面触发,当全部框架都加载完毕时在框架集上面触发,当图像加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<ocject>元素上面触发。
  • unload:当页面彻底卸载后在window上面触发,当全部框架都卸载后在框架上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
  • error:当发生javascript错误时在window上面触发,当没法加载图像时在<img>元素上面触发,当没法加载嵌入内容时在<object>元素上面触发,或者当有一或多个框架没法加载时在框架集上面触发。
  • select:当窗口或框架的大小变化时在window或框架上面触发。
  • resize:当窗口或框架的大小变化时在window或框架上面触发。
  • scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。
焦点事件

焦点事件会在页面得到或失去焦点时触发。利用这些事件并与document.hasFocus()方法及document.activeElement属性配合,能够知晓用户在页面上的行踪。

  • blur:在元素失去焦点时触发。这个事件不会冒泡,全部浏览器都支持它。
  • focus:在元素得到焦点时触发。这个事件不会冒泡,全部浏览器都支持它。
鼠标与滚轮事件
  • click:在用户单击主鼠标按钮或者按下回车键时触发。
  • dblclick:在用户双击主鼠标按钮时触发。
  • mousedown:在用户按下了任意鼠标按钮时触发。不能经过键盘触发这个事件。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围以内时触发。这个事件不冒泡,并且在光标移动到后代元素上不会触发。
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围以外时触发。这个事件不冒泡,并且在光标移动到后代元素上不会触发。
  • mousemove:当鼠标指针在元素内部移动试重复地触发。不能经过键盘触发这个事件。
  • mouseout:在鼠标指针位于一个元素上方,而后用户将其移入另个元素时触发。又移入的另外一个元素可能位于前一个元素的外部,也多是这个元素的子元素。
  • mouseover:在鼠标指针位于一个元素外部,而后用户将其首次移入另外一个元素边界以内时触发。
  • mouseup:在用户释放鼠标按钮时触发。

页面上的全部元素都支持鼠标事件。
除了mouseenter和mouseleave,全部鼠标事件都会冒泡,,也能够被取消,而取消鼠标事件将会影响浏览器的默认行为。

只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;若是mousedown和mouseup中的一个被取消,就不会触发click事件。
相似地,只有触发两次click事件,才会触发一次dblclick事件。
这四个事件触发的顺序始终以下:
(1)mousedown
(2)mouseup
(3)click
(4)mousedown
(5)mouseup
(6)click
(7)dblclick
显然,click和dblclick事件都会依赖于其余先行事件的触发;而mousedown和mouseup则不受其余事件的影响。

1.客户区坐标位置
鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。
它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。
2.页面坐标位置
经过客户区坐标可以知道鼠标是在视口中什么位置发生的,而页面坐标经过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,所以坐标是从页面自己而非视口的左边和顶边计算的。
在页面没有滚动的状况下,pageX和pageY的值与clientX和clientY的值相等。
3.屏幕坐标位置
鼠标事件发生时,不只会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置。而经过screenX和screenY属性就能够肯定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
4.鼠标按钮
只有在主鼠标按钮被单击时才会触发click事件,所以检测按钮的信息并非必要的。但对于mousedown和mouseup事件来讲,则在其event对象存在一个button属性,表示按下或释放的按钮。
DOM的button属性可能有以下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮(鼠标滚轮按钮),2表示次鼠标按钮。
4.触摸设备
ios和Android设备的实现很是特别,由于这些设备没有鼠标。

  • 不支持dblclick事件。双击浏览器窗口会放大画面,并且没有办法改变该行为。
  • 轻击可单击元素会触发mousemove事件。若是此操做会致使内容变化,将再也不有其余事件发生;若是屏幕没有所以变化,那么会依次发生mousedown、mouseup和click事件。轻击不可单击的元素不会触发任何事件。
  • mousemove事件也会触发mouseover和mouseout事件。
  • 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel和scroll事件。
键盘与文本事件

有3个键盘事件,简述以下。

  • keydown:当用户按下键盘上的任意键时触发,并且若是按住不放的话,会重复触发此事件。
  • keypress:当用户按下键盘上的字符键时触发,并且若是按住不放的话,会重复触发此事件。
  • keyup:当用户释放键盘上的键时触发。
触摸与手势事件-触摸事件

touchstart:当手指触摸屏幕时触发;即便已经有一个手指放在了屏幕上也会触发。
touchmove:档收视在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()能够阻止滚动。
touchend:当手指从屏幕上移开时触发。
touchcancel:当系统中止跟踪触摸时触发。
以上这几个事件都会冒泡,也都会取消。

触摸与手势事件-手势事件

ios2.0中的Safari还引入了一组手势事件。当两个手指触摸屏幕时就会产生手势,手势一般会改变显示项的大小,或者旋转显示项。

  • gesturestart:当一个手指已经按在屏幕上而另外一个手指又触摸屏幕时触发。
  • gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
  • gestureend:当任何一个手指从屏幕上面移开时触发。

内存和性能——事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。例如,click事件会一直冒泡到document层次。
若是可行的话,也能够考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。优势以下:

  • document对象很快就能够访问,并且能够在页面生命周期的任什么时候点上为它添加事件处理程序(无须等待DOMContentLoaded或load事件)。换句话说,只要可单击的元素呈如今页面上,就能够当即具有适当的功能。
  • 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
  • 整个页面占用的内存空间更少,可以提高总体性能。

最适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。虽然mouseover和mouseout事件也冒泡,但要适当处理它们并不容易,并且常常须要计算元素的位置。(由于当鼠标从一个元素移到其子节点时,或者当鼠标移出该元素时,都会触发mouseout事件。)

小结
事件是将Javascript与网页联系在一块儿的主要形式。“DOM3级事件”规范和HTML5定义了常见的大多数事件。即便有规范定义了基本事件,但不少浏览器仍然在规范以外实现了本身的专有事件,从而为开发人员提供更多掌握用户交互的手段。

  • 在使用事件时,须要考虑以下一些内存与性能方面的问题
  • 有必要限制一个页面中事件处理程序的数量,数量太多会致使占用大量内存,并且也会让用户感受页面反应不够灵敏。
  • 创建在事件冒泡机制之上的事件委托技术,能够有效地减小事件处理程序的数量。
  • 创建在浏览器卸载页面以前移除页面中的全部事件处理程序。

事件是Javascript中最重要的主题之一,深刻理解事件的工做机制以及它们对性能的影响相当重要。

好啦,事件就大概记到这里啦~

相关文章
相关标签/搜索