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

时间:2020-9-16 作者:admin

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


第13章 事件

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

事件流—事件冒泡

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

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

事件流—事件捕获

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

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

事件流—DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

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

事件处理程序

事件就是用户或浏览器自身执行的某种动作。诸如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,表示在冒泡阶段调用事件处理程序。

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()添加的匿名函数将无法移除。就像上面的例子一样。

事件处理程序——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对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。所有的事件都会有下表列出的成员。

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


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

在事件处理程序内部,对象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中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响至关重要。

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

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。