• JavaScript高级程序设计:第十三章


    第十三章

    一、理解事件流

             事件流描述的是从页面中接收事件的顺序。

    1.事件冒泡

             IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。以下面的HTML页面为例:

             <!DOCTYPE html>

    <html>

             <head>

                      <title>Event Bubling Example</title>

             </head>

             <body>

                      <div id="myDiv">Click Me</div>

             </body>

    </html>

             如果你单机了页面中的<div>元素,那么这个click事件会按照如下顺序传播:

             ①<div>

             ②<body>

             ③<html>

             ④document

             也就是说,click事件首先在<div>元素上发生,而这个元素就是我们单击的元素。然后click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。

    2.事件捕获

             另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早的接收到事件。如果仍以前面的页面作为演示事件捕获的例子,那么单击<div>元素就会以下列顺序触发click事件。

             ①document

             ②<html>

             ③<body>

             ④<div>

             在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树依次向下,一直传播到事件的实际目标,即<div>元素。

    3.DOM事件流

             “DOM2级事件”规定的事件流包括三个阶段:事件的捕获阶段、处于目标阶段和事件冒泡阶段。

    二、事件处理程序

    1.HTML事件处理程序

    某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。        

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

    <script  type = “text/javascript”>

             function  showMessage(){

                      alert(“Hello world!”);

             }

    </script>

    <input  type = “button”  value = “Click  Me”  onclick = “showMessage()”  />

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

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

    <!—输出“click”-->

    <input type=”button” value=”click Me” onclick =”alert(event.type)”>

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

    <!—输出”Click Me”-->

    <input type=”button” value=”Click Me” onclick=”alert(this.value)”>

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

    2.DOM0级事件处理程序

             通过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。

             每个元素,都有自己的事件处理程序属性,这些属性通常全部小写,例如onclick。将这种属性的值设置为一个函数,就可以指定事件处理程序,如下所示:

             var btn = document.getElementById(“myBtn”) ;

             btn.onclick = function() {

                      alert(“Clicked”) ;

             } ;

             在此,我们通过文档对象取得了一个按钮的引用,然后为它指定了onclick事件处理程序。使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的this引用当前元素。来看一个例子:

             var btn = document.getElementById(“myBtn”) ;

             btn.onclick = function() {

                      alert(this.id) ;         //”myBtn”

             } ;

             单击按钮显示的是元素的ID,这个ID是通过this.id取得的。

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

             btn.onclick = null ; //删除事件处理程序

    3.DOM2级事件处理程序

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

             使用DMO2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。来看下面的例子。

             var btn = document.getElementById("myBtn");

    btn.addEventListener("click",function(){

             alert(this.id);

    },false);

    btn.addEventListener("click",function(){

             alert("Hello world!");

    },false);

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

    4.IE事件处理程序

             IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接收相同的2个参数:事件处理程序名称好事件处理程序函数。与DOM方法不同的是:

             (1)DOM0级方法事件处理程序会在其所属元素的作用域中运行,而attachEvent()方法在全局作用域中运行。

             (2)attachEvent()事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。

    5.跨浏览器的事件处理程序

             第一个要创建的方法是addHandler(),它的职责是视情况分别使用DOM0级方法、DOM2级方法或IE方法来添加事件。这个方法属于一个名叫EventUtil的对象,它接受3个参数:要操作的元素、事件名称和事件处理程序函数。

             与addHandler()对应的方法是removeHandler(),它也接受相同的参数。这个方法的职责是移除之前添加的事件处理程序——无论该事件处理程序是采取什么方式添加到元素中的,如果其他方法无效,默认采用DOM0级方法。

    三、事件对象

    1.DOM中的事件对象

             兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法,都会传入event对象。来看下面的例子:

             var btn = document.getElementById(“myBtn”) ;

             btn.onclick = function(event){

               alert(”event.type”);

             };

             btn.addEventListener(“click” ,function(event){

    alert(event.type);      //“click”

    },false);

    这个例子中的两个事件处理程序都会弹出一个警告框,显示由event.type属性表示的事件类型。

    2.IE中的时间对象

             与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。来看下面的例子:

             var btn = document.getElementById(“myBtn”) ;

             btn.onclick = function() {

                      var event = window.event ;

                      alert(event.type) ;       //”click”

             };

             通过window.event取得了event对象,并检测了被触发事件的类型。

    3.跨浏览器的事件对象

             虽然DOM和IE中的event对象不同,但基于它们之间的相似性依旧可以拿出跨浏览器的方案来。IE中event对象的全部信息和方法DOM对象中都有,只不过实现方式不一样。不过,这种对应关系让实现两种事件模型之间的映射非常容易。

    4.事件类型

    (1)UI事件,当用户与页面上的元素交互时触发;

    (2)焦点事件,当元素获得或是去焦点时触发;

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

    (4)滚轮事件,当使用鼠标滚轮时触发;

    (5)文本事件,当在文档中输入文本时触发;

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

    (7)合成事件,当为IEM输入字符时触发;

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

    (9)变动名称事件,当元素或属性名变动时触发。此类事件已经被废弃,没有任何浏览器实现它们。

    五、性能和内存

    1.事件委托

             对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click事件会一直冒泡到document层次。也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。以下面的HTML代码为例:

    <ul id=”myLinks”>

             <li id=”goSomewhere”>Go somewhere</li>

             <li id=”doSomething”>Do something</li>

             <li id=”sayHi”>Say hi</li>

    </ul>

             其中包含3个被单击后会执行操作的列表项。按照传统做法,需要像下面这样为它们添加3个事件处理程序。

    var item1=document.getElementById("goSomewhere");

    var item2=document.getElementById("doSomething");

    var item3=document.getElementById("sayHi");

    EventUtil.addHandler(item1,"click",function(event){

             location.href="http://www.wrox.com";

    });

    EventUtil.addHandler(item2,"click",function(event){

             document.title="I changed the document's title";

    });

    EventUtil.addHandler(item3,"click",function(event){

             alert("hi");

    });

             如果在一个复杂的web应用程序中,对所有可单击的元素都采用这种方式,那么结果就会有数不清的代码用于添加事件处理程序。此时,可以利用事件委托技术解决这个问题。使用事件委托,只需在DOM树中尽量最高的层次上添加一个事件处理程序,如下面的例子。

    var list=document.getElementById("myLinks");

    EventUtil.addHandler(list,"click",function(event){

             event=EventUtil.getEvent(event);

             var target=EventUtil.getTarget(event);

             switch(target.id){

                      case "doSomething";

                              document.title="I changed the document's title";

                              break;

                      case "goSomewhere";

                              location.href="http://www.wrox.com";

                              break;

                      case "sayHi";

                              alert("hi");

                              break;

             }

    });

    2.移除事件处理程序

             每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的javascript代码之间就会建立一个连接。这种连接越多,页面执行起来就越慢。如前所述,可以采用事件委托技术,限制建立的链接数量。另外,在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。内存中留有那些过时不用的“空事件处理程序”,也是造成Web应用程序内存与性能问题的主要原因。

             在两种情况下,可能会造成上述问题:第一种情况就是从文档中移除带有事件处理程序的元素时。第二种情况就是卸载页面的时候。

    六、模拟事件

    1.DOM中的事件模拟

             可以在document对象上使用createEvent()方法创建event对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。在DOM2级中所有这些字符串都是使用英文复数形式,而在DOM3级中变成了单数。

    (1)模拟鼠标事件

             创建新的鼠标事件对象并为其指定必要的信息就可以模拟鼠标事件。创建鼠标事件对象的方法是为createEvent()传入字符串“MouseEvents”。返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。这个方法接收15个参数。下面, 我们通过一个例子了解如何模拟对按钮的单击事件:

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

    (2)模拟键盘事件

             DOM3级规定,调用createEvent()并传入“keyboardEvent”就可以创建一个键盘事件。返回的事件对象会包含一个initKeyEvent()方法。

             由于DOM3级不倡导使用keypress事件,因此只能利用这种技术来模拟keydown和keyup事件。

             var textbox=document.getElementyById(“myTextbox”),event;

             //以DOM3级方式创建事件对象

             if (document.implementation.hasFeature(“KeyboardEvents”,”3.0”)){

                      event=document.createEvent(“KeyboardEvent”);

                      //初始化事件对象

                      event.initKeyboardEvent(“keydown”,true,true,document.defaultView,”a”,0,”shift”,0);

                      }

                      //触发事件

                      textbox.dispatchEvent(event);

             这个例子模拟的是按住shift键的同时又按下A键。

             在其他浏览器中,则需要创建一个通用的事件,然后再向事件对象中添加键盘事件特有的信息。例如:

             var textbox=document.getElementById(“myTextbox”);

             //创建事件对象

             var event=document.createEvent(“Events”);

             //初始化事件对象

             event.initEven(type,bubbles,cancelable);

             event.view=document.defaultView’

             event.altKey=false;

             event.ctrlKey=false;

             event.shiftKey=false;

             event.metaKey=false;

             event.keyCode=65;

             event.charCode=65;

             //触发事件

             textbox.dispatchEvent(event);

    3.模拟其他事件

             虽然鼠标事件和键盘事件是在浏览器中最经常模拟的事件,但有时候同样需要模拟变动事件和HTML事件。要模拟变动事件,可以使用createEvent(“MutationEvents”)创建一个包含initMutationEvent()方法的变动事件对象。例如:

             var event=document.createEven(“MutationEvents”) ;

             event.initMutationEvent(“DOMNodeInserted”,true,false,someNode,””,””,””,0);

             target.dispatchEvent(event);

             以上代码模拟了DOMNodeInserted事件。其他变动事件可以按照这个样子来模拟,只要改一改参数。

    4.自定义DOM事件

             自定义事件不是由DOM原生触发的,它的目的是让开发人员创建自己的事件。要创建新的定义事件,可以调用createEvent(“CustomEvent”)。返回的对象有一个名为initCustomEvent()的方法。

    2.IE中的事件模拟

             调用document.createEventObject()方法可以在IE中创建event对象。这个方法不接受参数,结果会返回一个通用的event对象。然后,手工为这个对象添加所有必要的信息。最后一步就是在目标上调用fireEvent()方法,这个方法接受两个参数:事件处理程序的名称和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性;其他属性则都必须通过手工添加。

  • 相关阅读:
    A1023 Have Fun with Numbers [大整数乘法]
    大整数的四则运算
    A1096 Consecutive Factors [因子分解]
    A1078 Hashing [质数和散列结合]
    A1015 Reversible Primes [质数问题]
    又谈进制转换
    A1088 Rational Arithmetic [分数四则运算]
    A1081 Rational Sum [分数计算]
    linux主流系统配置静态ip
    主机ping虚拟机请求超时,虚拟机ping主机正常ping通导致ssh连接问题
  • 原文地址:https://www.cnblogs.com/koto/p/5138217.html
Copyright © 2020-2023  润新知