web前端之精通dojo五:利用Dojo进行DOM事件编程

来源:互联网 发布:豆瓣2017网络剧top10 编辑:程序博客网 时间:2024/04/29 10:00

web前端之精通dojo五:利用Dojo进行DOM事件编程

如果针对浏览器编程,让它动态地做任何事,就必须将代码与DOM事件关联起来。这时你会发现有两种不同的事件API可用。不干预下风的IE提供了第三种API————同时还附送了内存泄漏问题。通过深入学习这些API,可以了解浏览器间的不兼容性以及各浏览器的特性。事件编程很快就成为了让人头疼的问题。

好在,Dojo为我们提供了一个完备的事件编程框架来解决这些问题。但是事件并不是Dojo异步编程的全部,而只是异步编程模型的一部分。Dojo还包括了一些使用这些模型的函数族,利用这些函数可以创建各种类型的有趣而强大的应用程序。

利用Dojo进行DOM事件编程

Dojo提供了一个DOM事件框架,该框架在所有Dojo支持的浏览器中具有一致的行为。Dojo更是修复了IE免费提供的内存泄漏问题。

下面概述一下事件驱动编程模型。事件发生时,将调用一个函数————称为处理函数。时间有时被称为信号,可能是一个硬件事件,例如鼠标手势;或是一个软件事件,例如提交表单。处理函数又被称为监听器或回调函数,是一个拥有明确定义的参数集合并在明确定义的上下文中执行的函数。

处理函数签名

处理函数也是函数,Dojo事件框架为处理函数提供了单一的参数,称为事件对象。该对象包含一些属性和方法,这些属性和方法按照W3C事件模型的规定描述和控制事件————这对于没有本地实现这一模型的IE也是一样的。如果你要编写一个不需要事件对象的处理函数,只需要声明一个不带参数的函数。由于要记录事件对象的信息,因此我们的处理函数要接受事件对象作为唯一的参数。

function handleClick(eventObj){    console.log(        "Event("+eventObj.type+") on Dom node "+eventObj.target.id+";currentTarget= "+eventObj.currentTarget.id    );}

有三种类型的事件对象构成一个继承单链:MouseEvent继承自UIEvent,UIEvent继承自Event.
这里写图片描述

要注意我们的处理函数没有返回值。处理函数都不需要返回值,因为它的任何返回值都将被忽略。

事件传播

对于有些事件,浏览器会把一个事件分派到多个DOM节点,这一过程称为事件传播。处理函数可以影响事件的传播,理解事件传播对于优化处理函数代码非常重要。

一般点击事件的过程是,当用户点击一个DOM节点时,一个点击事件被发送到目标节点(目标节点就是事件发生的节点)、它的父节点、祖父节点等等。一直到文档树的最上层。这个发送的过程叫做冒泡。

冒泡使我们可以将代码统一放到父节点。例如,假设我们有一个div节点,它含有100个子节点,每个子节点在结构上都是一致的并都使用同一个点击事件处理函数。我们可以为每一个子节点都关联这个处理函数,也可以只为它们的父节点div关联这个处理函数。父节点所关联的处理函数要检查事件对象的target属性,以此确定实际被点击的子节点。这个解决方法显然更胜一筹。

function someHandler(eventObj){    /*.....*/    eventObj.stopPropagation();}

有时候你可能想阻止冒泡行为。例如,树结构中的一个底层节点的处理函数调用了它祖父节点的处理函数来处理一下事件,那么我们肯定不希望祖父节点的处理函数因为冒泡行为再次被调用。

关于冒泡和捕获等:查看详情。

(1)捕获阶段
(2)调用目标节点的处理函数
(3)冒泡阶段

我们可以在事件处理过程中的任何地点利用任何处理函数阻止事件传播,只需要调用事件对象的stopPropagation方法。同样我们也可以跳过捕获阶段而不影响后面的两个阶段,这也正是Dojo事件框架的工作方式。这是一件非常好的事。当你思考之后救火发现捕获行为往往是错误的,捕获意味着通用的处理方式。

我们要增强点击处理函数,使它在按下shift键时不会冒泡:

function handleClick(eventObj){    console.log(        "Event("+eventObj.type+") on Dom node "+eventObj.target.id+";currentTarget= "+eventObj.currentTarget.id    );}//eventObj的类型为MouseEventif(eventObj.shiftKey){    // 在按下shift键时点击将阻止冒泡    eventObj.stopPropagation();}

随后我们就将这个处理函数与一个小型DOM树中的所有节点关联。这个示例会先显示冒泡行为(当点击时),然后会立刻阻止冒泡行为(当按下shift点击)

默认处理

无论是否有处理函数与之关联,每一个DOM事件都会引发浏览器执行一些默认处理行为。事件处理函数可以通过调用事件对象的preventDefault方法取消默认行为:

function someHandler(eventObj){    eventObj.preventDefault();}

最后介绍一下十分好用的函数dojo.stopEvent(event),它同时调用event.preventDefault()和event.stopPropagation()这两个函数

function someHandler(eventObj){    /*...*/    dojo.stopEvent(eventObejct);    //完全等价于    eventObj.stopPropagation();    eventObj.preventDefault();}

关联处理函数

Dojo提供了一个用来关联函数和DOM事件的dojo.connect方法。这个函数是Dojo事件框架的关键,因为它使事件处理函数按我们所描述的那样去执行,而且不依赖任何特定的浏览器。他的签名如下:

handle=dojo.connect(obj,event,context,handler);

从本质上讲,调用dojo.connect关联一个处理函数到一个DOM节点事件,就如同调用W3C DOM的obj.addEventListener(event,dojo.hitch(context,handler))。而且,即使当前浏览器不直接支持addEventListener的IE版本,Dojo也能做到同样的事情。

dojo.connect(obj,event,context,handler)的前面两个参数obj(一个DOM节点)和event(一个字符串)定义了要关联的事件。接下来的两个参数,context(可选,是一个对象)和handler(一个函数或者是字符串),定义了处理函数。这里context参数是语法糖衣,它是我们通过绑定函数到上下文(例如,绑定一个方法到一个对象)得到一个处理函数时,不需要显示调用dojo.hitch。我们将会看到这一模式在Dojo被频繁地使用。如果dojo.hitch让你一头雾水,就看前一篇博客。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>关联处理函数</title><script type="text/javascript" src="../../../dojoroot/dojo/dojo.js" djConfig="parseOnLoad:true"></script><script type="text/javascript">function handleClick(eventObj){    console.log(        "Event("+eventObj.type+") on Dom node "+eventObj.target.id+";currentTarget= "+eventObj.currentTarget.id    );}function connectAll(){    dojo.connect(dojo.byId("body"),"click",handleClick);    dojo.connect(dojo.byId("body-div"),"click",handleClick);    dojo.connect(dojo.byId("body-div-p"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-1"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-2"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-3"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-4"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-5"),"click",handleClick);    //上面的代码可以替换成    //dojo.query("body *").connect("click",handleClick);    //后续会讲到}function myClick1(event){}function myClick2(event){}dojo.connect(someDomNode,"click",function(event){    myClick1(event),myClick2(event);});</script></head><body id="body">    <div id="body-div">        <p id="body-div-p">            是理解事件的时候了        </p>        <ol id="body-div-ol">            <li id="doby-div-ol-li-1">                利用dojo.connect/disconnect链接/取消链接处理函数            </li>            <li id="doby-div-ol-li-2">                总是向处理函数传递一个事情对象作为参数            </li>            <li id="doby-div-ol-li-3">                不要用“this”引用当前目标            </li>            <li id="doby-div-ol-li-4">                能冒泡的事件总是冒泡            </li>            <li id="doby-div-ol-li-5">                完整的定义了键盘事件和相关的事件对象            </li>        </ol>    </div></body></html>

利用connect可以将多个不同的处理函数关联到相同的(node,event)事件。这种情况下,处理函数的调用顺序不确定。

这样我们就可以把创建的处理函数与事件关联。

利用dojo.addOnLoad执行初始化代码

当我们调用dojo.connect把处理函数链接到Dom节点时,最好确保DOM树已经由浏览器创建,而且处理函数引用的所有其他JavaScript代码都已被下载并求值。我们需要确保dojo.required引用的JavaScript资源和DOM树已经可用。上述问题可以用dojo.addOnLoad解决。

dojo.addOnLoad接受一个函数作为参数并且确保该函数在满足下述三个条件后立即执行。

1.DOM树被浏览器创建并可以被客户代码使用。注意,这并不表示所有资源(例如图片)都被载入

2.所有的通过Dojo加载器请求的JavaScript资源都被载入

3.所有的Dojo小部件都被解析完毕

dojo.addOnLoad可以被调用任意多次,所有作为参数传入的函数严格按照他们被注册的顺序执行。dojo.addOnLoad的调用参数可以是一个函数的引用或是一个对象的函数名。下面是每种用法的示例:

下面是事件示例的完整代码,其中包含对dojo.addOnLoad的调用:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>利用dojo.addOnLoad执行初始化代码</title><script type="text/javascript" src="../../../dojoroot/dojo/dojo.js" djConfig="parseOnLoad:true"></script><script type="text/javascript">// dojo.addOnLoad(f);//函数f// dojo.addOnLoad(function(){});//一个函数字面量// dojo.addOnLoad(o,"f");//函数o["f"]function handleClick(eventObj){    console.log(        "Event("+eventObj.type+") on Dom node "+eventObj.target.id+";currentTarget= "+eventObj.currentTarget.id    );    // eventObj的类型是MouseEvent    if(eventObj.shiftKey){        //按下shift键并点击鼠标将阻止事件冒泡        eventObj.stopPropagation();    }}function connectAll(){    dojo.connect(dojo.byId("body"),"click",handleClick);    dojo.connect(dojo.byId("body-div"),"click",handleClick);    dojo.connect(dojo.byId("body-div-p"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-1"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-2"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-3"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-4"),"click",handleClick);    dojo.connect(dojo.byId("body-div-ol-li-5"),"click",handleClick);}dojo.addOnLoad(connectAll);</script></head><body id="body">    <div id="body-div">        <p id="body-div-p">            是理解事件的时候了        </p>        <ol id="body-div-ol">            <li id="doby-div-ol-li-1">                利用dojo.connect/disconnect链接/取消链接处理函数            </li>            <li id="doby-div-ol-li-2">                总是向处理函数传递一个事情对象作为参数            </li>            <li id="doby-div-ol-li-3">                不要用“this”引用当前目标            </li>            <li id="doby-div-ol-li-4">                能冒泡的事件总是冒泡            </li>            <li id="doby-div-ol-li-5">                完整的定义了键盘事件和相关的事件对象            </li>        </ol>    </div></body></html>

以上就是关于Dojo DOM事件框架的全部内容:

1.dojo.connect/disconnect链接或取消链接处理函数

2.处理函数通常要接收一个与事件相关的事情对象以获取事件的详细信息

3.使用事件对象的target/currentTarget属性获取目标/当前目标DOM节点,而不使用this

4.能冒泡的事件通常都要冒泡,而捕获阶段通常被禁止

5.定义了键盘事件及其关联的事件对象

6.利用dojo.addOnLoad对那些把处理函数连接到事件的初始化函数进行注册

DOM事件分类

这里写图片描述

传给处理函数的事件对象的类型

事件是否支持冒泡(B表示支持)

事件相关默认处理过程能否被取消

1 0