《JavaScript高级程序设计》读书笔记(七):事件

1.事件流

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

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

1.事件冒泡

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

 
    
    
        Event Bubbling Example
    
    
        
Click Me
    
    
以这个为例:

《JavaScript高级程序设计》读书笔记(七):事件_第1张图片

2.事件捕获

Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。 

《JavaScript高级程序设计》读书笔记(七):事件_第2张图片


虽然事件捕获是 Netscape Communicator 唯一支持的事件流模型,但 IE9、Safari、Chrome、Opera和 Firefox 目前也都支持这种事件流模型。尽管“DOM2 级事件”规范要求事件应该从 document 对象开始传播,但这些浏览器都是从 window 对象开始捕获事件的。由于老版本的浏览器不支持,因此很少有人使用事件捕获。我们也建议读者放心地使用事件冒泡,在有特殊需要时再使用事件捕获。

3.DOM事件流

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

元素会按照图13-3所示顺序触发事件。 

《JavaScript高级程序设计》读书笔记(七):事件_第3张图片

在 DOM 事件流中,实际的目标(

元素)在捕获阶段不会接收到事件。这意味着在捕获阶段, 12事件从 document 到再到后就停止了。下一个阶段是“处于目标”阶段,于是事件在
上发生,并在事件处理(后面将会讨论这个概念)中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。 

多数支持 DOM 事件流的浏览器都实现了一种特定的行为;即使“DOM2 级事件”规范明确要求捕获阶段不会涉及事件目标,但 IE9、Safari、Chrome、Firefox 和 Opera 9.5 及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。 

2.事件处理程序

onClick的方式会在事件流的冒泡阶段被处理

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

通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过 addEventListener()添加的匿名函数将无法移除,如下面的例子所示。

    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
        alert(this.id);

}, false);

//这里省略了其他代码

btn.removeEventListener("click", function(){ //没有用!

alert(this.id);

}, false); 

大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,我们不建议在事件捕获阶段注册事件处理程序。 

在 IE 中使用 attachEvent()与使用 DOM0 级方法的主要区别在于事件处理程序的作用域。在使用 DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用 attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window。来看下面的例子。

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

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

});

在编写跨浏览器的代码时,牢记这一区别非常重要。


3.事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的 3信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。例如,鼠标操作导致的事件对象中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。 


4.事件类型

  •   UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发;

  •   焦点事件,当元素获得或失去焦点时触发;

  •   鼠标事件,当用户通过鼠标在页面上执行操作时触发;

  •   滚轮事件,当使用鼠标滚轮(或类似设备)时触发;

  •   文本事件,当在文档中输入文本时触发;

  •   键盘事件,当用户通过键盘在页面上执行操作时触发;

  •   合成事件,当为 IME(Input Method Editor,输入法编辑器)输入字符时触发;

  •   变动(mutation)事件,当底层 DOM 结构发生变化时触发。 

1.UI事件

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

  •   load:当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在元素上面触发,或者当嵌入的内容加载完毕时在元素上面触发。

  •   unload:当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在元素上面触发。

  •   abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。

  •   error:当发生 JavaScript 错误时在 window 上面触发,当无法加载图像时在元素上面触发,当无法加载嵌入内容时在元素上面触发,或者当有一或多个框架无法加载时在框

    架集上面触发。第 17 章将继续讨论这个事件。

  •   select:当用户选择文本框()中的一或多个字符时触发。

  •   resize:当窗口或框架的大小变化时在 window 或框架上面触发。 

  •   scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的滚动条。

  • 2.焦点事件

     blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。

     DOMFocusIn:在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。只有Opera 支持这个事件。DOM3 级事件废弃了 DOMFocusIn,选择了 focusin。

     DOMFocusOut:在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。只有 Opera支持这个事件。DOM3 级事件废弃了 DOMFocusOut,选择了 focusout。 

     focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
     focusin:在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。

     focusout:在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。

    当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:

    (1) focusout 在失去焦点的元素上触发;
    (2) focusin 在获得焦点的元素上触发;
    (3) blur 在失去焦点的元素上触发;

    (4) DOMFocusOut 在失去焦点的元素上触发;
    (5) focus 在获得焦点的元素上触发;
    (6) DOMFocusIn 在获得焦点的元素上触发。 

    3.鼠标与滚轮事件

    click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着 onclick 事件处理程序既可以通过键盘也可以通过鼠标执行。

    dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。从技术上说,这个事件并不是 DOM2 级事件规范中规定的,但鉴于它得到了广泛支持,所以 DOM3 级事件将其纳入了标准。

    mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
    mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它

    纳入了规范。IEFirefox 9+ Opera 支持这个事件。
    mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且

    在光标移动到后代元素上不会触发。DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它

    纳入了规范。IEFirefox 9+ Opera 支持这个事件。
    mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。 

    mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。

    mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件。

    mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。 

    只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件;如果mousedown 或 mouseup 中的一个被取消,就不会触发 click 事件。类似地,只有触发两次 click 事件,才会触发一次 dblclick 事件。如果有代码阻止了连续两次触发 click 事件(可能是直接取消 click事件,也可能通过取消 mousedown 或 mouseup 间接实现),那么就不会触发 dblclick 事件了。这 4个事件触发的顺序始终如下:

    (1) mousedown

    (2) mouseup

    (3) click
    (4) mousedown

    (5) mouseup

    (6) click
    (7) dblclick


    5.模拟事件

    可以使用 JavaScript 在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样。也就是说,这些事件该冒泡还会冒泡,而且照样能够导致浏览器执行已经指定的处理它们的事件处理程序。在测试 Web 应用程序,模拟触发事件是一种极其有用的技术。 

    可以在 document 对象上使用 createEvent()方法创建 event 对象。这个方法接收一个参数,即

    表示要创建的事件类型的字符串。在 DOM2 级中,所有这些字符串都使用英文复数形式,而在 DOM3 级中都变成了单数。这个字符串可以是下列几字符串之一。

    •   UIEvents:一般化的 UI 事件。鼠标事件和键盘事件都继承自 UI 事件。DOM3 级中是 UIEvent。

    •   MouseEvents:一般化的鼠标事件。DOM3 级中是 MouseEvent。

    •   MutationEvents:一般化的 DOM 变动事件。DOM3 级中是 MutationEvent。

    •   HTMLEvents:一般化的 HTML 事件。没有对应的 DOM3 级事件(HTML 事件被分散到其他类别中)。

    例子:
    var btn = document.getElementById("myBtn");
    

    //创建事件对象
    var event = document.createEvent("MouseEvents");

    //初始化事件对象
    event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,false, false, false, false, 0, null);

    btn.dispatchEvent(event);

    6.总结

    在使用事件时,需要考虑如下一些内存与性能方面的问题。
     有必要限制一个页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户感觉页面反应不够灵敏。

     建立在事件冒泡机制之上的事件委托技术,可以有效地减少事件处理程序的数量。 建议在浏览器卸载页面之前移除页面中的所有事件处理程序。

    可以使用 JavaScript 在浏览器中模拟事件。“DOM2 级事件”和“DOM3 级事件”规范规定了模拟事件的方法,为模拟各种有定义的事件提供了方便。此外,通过组合使用一些技术,还可以在某种程度上模拟键盘事件。IE8 及之前版本同样支持事件模拟,只不过模拟的过程有些差异。 


    你可能感兴趣的:(前端)