INDEX-HTML

《dojo 边学边用》(03), 基本的常用方法介绍(dojo.byId / dojo.query / dojo.addOnLoad / dojo.connect / dojo.disconnect / dojo.forEach)

dojo.byId




dojo.query

--
开发人员需要更加方便的进行 DOM 查询的方法。Dojo 中提供了 dojo.query 库,用来方便的进行 DOM 查询。dojo.query 的基本用法是使用 CSS 3 的选择器语法来选择 HTML 文档中的节点。对于复杂的查询条件,可以用复杂的 CSS 选择器来描述。使用 dojo.query 可以极大的降低代码量。比如上面提到的例子,用 dojo.query 的话只需要一行代码就足够了:dojo.query("#myDiv span")。另外 dojo.query 使用的是 CSS 的选择器语法,这对于开发人员来说并不陌生。代码清单 中给出了一些常用的 dojo.query 的用法。

dojo.query("#header > h1") //ID 为 header 的元素的直接子节点中的 h3 元素
dojo.query("span[title^='test']") // 属性 title 以字符串 test 开头的 span 元素
dojo.query("div[id$='widget']") // 属性 id 以字符串 widget 结尾的 div 元素
dojo.query("input[name*='value']") // 属性 name 包含子串 value 的 input 元素
dojo.query("#myDiv, .error") // 组合查询,结果中包含 ID 为 myDiv 的元素和 CSS 类为 error 的元素
dojo.query(".message.info") // 同时包含了 CSS 类 message 和 info 的元素,注意两个类之间不包含空格
dojo.query("tr:nth-child(even)")// 出现在父节点的偶数位置的 tr 元素
dojo.query("input[type=checkbox]:checked") // 所有选中状态的复选框
dojo.query(".message:not(:nth-child(odd))") // 嵌套子查询,选中包含 CSS 类 message,
//并且不出现在父节点的奇数位置的元素

 

--

dojo.query方法除了第一个必须的参数用来表示所用的选择器语法之外,还有一个可选的参数用来指定查询的范围,可以是一个 ID 或是元素。如果传入该参数,则查询结果中只包含该元素的子节点。默认的查询范围是整个文档树。如 dojo.query("span.info", "myDiv")只在 ID 为 myDiv的元素的子节点中查询包含 CSS 类 info的 span 元素。熟练使用 dojo.query 的前提条件是对 CSS 3 规范定义的选择器语法比较熟悉。关于 CSS 3 选择器语法的更多信息,请见 参考资料。

 

dojo.query 的另外一个强大功能是可以对选择出来的节点进行统一处理。通过方法级联还可以写出非常简洁的代码。下面的章节中将会详细介绍 dojo.query 的这一能力。

--




dojo.addOnLoad

 

dojo.addOnLoad接受一个函数作为参数。虽然可以像var init = function(){  /*...you code...*/ }这样先定义个函数,然后再将init传递给dojo.addOnLoad,


【一般来讲,页面加载事件指的可能是Window的onload事件,也可能是Mozilla系列浏览器的DOMContentLoaded事件完成】
addOnLoad在页面中的部件没有被解析完成之前是不会执行的(前提是已经通过配置告诉Dojo在页面加载后解析部件)

dojo.addOnLoad(function(){
//需要等到相关js和dom加载完成后执行的代码
//依赖于dojo.require语句的内容放在这里...
});

在dojo.addOnLoad执行之前必须要执行的两项基本操作,顺序不一定是这样的。1,平台配置,根据我们再djConfig中指定的自定义配置选项做出相应处理。

前面已经讲过了,djConfig可以是一个关联数组,该数组必须在加载dojo的script标签之前定义,或者也可以作为加载dojo的script标签的一个属性存在。执行针对浏览器的功能增强操作,(例如,在使用dojo不同的ajax实用程序执行异步调用时,创建一个XMLHttpRequest (XHR)对象;通过标准化DOM时间、标准化键码映射,以及采取其他减少或防止内存泄露的手段来修补浏览器的不兼容性的行为。)2,命名空间的建立和加载,建立dojo命名空间,以确保工具箱提供的各种实用

 

程序不会与页面中已经存在的标识符发生冲突。通过组成Base的各种函数和标识符加载dojo命名空间。

 

 

===尽管与dojo.addOnLoad相比,dojo.addOnUnload没有那么重要,但是如果某些逻辑需要在页面卸载的时候执行,那么dojo.addOnUnload依然是最佳选择。

 

dojo.ready,只是dojo.addOnLoad的一个别名,用法和addOnLoad一样!



dojo.connect

 

事件侦听器,在dojo中通过明确的连接函数 和/或 DOM事件可以简历一个直接的通信渠道,从而在一个事件发生时自动触发另一个事件。
可以使用dojo.connect连接一些列事件。每次调用dojo.connect都会返回一个句柄(handle)对象,该对象可以再将来需要断开连接的时候传递给dojo.disconnect。
【虽然:等页面卸载时所有句柄对象中的连接都能自动断开,但在一个长期运行的应用程序大量使用临时连接的情况下,手工管理这些句柄会更有利于避免内存泄露,IE尤为突出】

 

/*建立连接*/
dojo.connect(
/*Object | null*/ obj,
/*String*/ event,
/*Object | null*/ context,
/*String | Function*/ method); //返回一个句柄对象

下面通过一个例子来讲一下dojo.connect的用法:

Tom and Jerry
//Tom and Jerry“猫和老鼠”我们小时候都看过的
//在下面这个空房子里,有Tom 和 Jerry
function Tom() {
this.greet = function () {
alert(
"Hi ,I'm Tom");
}
}

function Jerry() {
this.hello = function () {
alert(
"Hello ,I'm Jerry");
}
}

tom
= new Tom();
jerry
= new Jerry();

//执行tom.greet()让tom在空房子里向大家打了个招呼
tom.greet();

 

 

            //就算tom不知道jerry是否存在,jerry听到了也应该回应一下
            //要解决这个难题,其实质要一行代码即可。将上面的代码修改为如下即可

//上面的代码执行之后,会在页面中弹出  Hi ,I'm Tom

//就算tom不知道jerry是否存在,jerry听到了也应该回应一下            
//要解决这个难题,其实质要一行代码即可。将上面的代码修改为如下即可

 

 

 

Tom and Jerry
//Tom and Jerry“猫和老鼠”我们小时候都看过的
//在下面这个空房子里,有Tom 和 Jerry
function Tom() {
this.greet = function () {
alert(
"Hi ,I'm Tom");
}
}

function Jerry() {
this.hello = function () {
alert(
"Hello ,I'm Jerry");
}
}

tom
= new Tom();
jerry
= new Jerry();

var handle = dojo.connect(tom, "greet", jerry, "hello"); //建立Tom和Jerry的连接
tom.greet(); //现在,jerry听到招呼自动回应了!

 

上面的代码执行之后会在页面中一次弹出:Hi ,I'm Tom / Hello ,I'm Jerry

上面代码中特殊的标红,加大字号的handle为我们声明的一个变量handle,为dojo.connect方法执行所返回的的句柄对象。
要想简单的理解dojo.content返回的handle句柄,我们可以把它想象成一个黑盒子,因为除了将来把它传递给dojo.disconnect 销毁这个连接之外,根本用不到它。

 

那么下面我就来试试使用这个handle销毁dojo.connect所建立的Tom猫和Jerry老鼠的连接吧:

Tom and Jerry-disconnect
//Tom and Jerry“猫和老鼠”我们小时候都看过的
//在下面这个空房子里,有Tom 和 Jerry
function Tom() {
this.greet = function () {
alert(
"Hi ,I'm Tom");
}
}

function Jerry() {
this.hello = function () {
alert(
"Hello ,I'm Jerry");
}
}

tom
= new Tom();
jerry
= new Jerry();

var handle = dojo.connect(tom, "greet", jerry, "hello"); //建立Tom和Jerry的连接
tom.greet(); //现在,jerry听到招呼自动回应了!

dojo.disconnect(handle);
//通过调用disconenct方法传入handle句柄来销毁之前建立的连接
tom.greet();//这下等待tom的只有沉默了

 

上面的代码执行之后会在页面中依次次弹出:Hi ,I'm Tom / Hello ,I'm Jerry 在销毁handle之后,弹出 Hi ,I'm Tom

 


使用dojo.connect为事件建立连接不仅简单,而且代码页清晰,容易维护,没有冗余操作,没有曲折离奇的代码,无需搭配自己的解决方案,更没有难以维护的噩梦。

 
下面介绍一下dojo.connect提供的一个自动传递参数的特性既可以将参数从第一个环境的函数传递给第二个环境的函数。多么神奇呀,下面我们来讲一下这个特性:

Nice to meet you
//Tom and Jerry“猫和老鼠”我们小时候都看过的
//在下面这个空房子里,有Tom 和 Jerry
function Tom() {
this.greet = function (something) {//参数something
alert("Hi ,I'm Tom。 " + something);
}
}

function Jerry() {
this.hello = function (something) {
alert(
"Hello ,I'm Jerry。" + something);
}
}

tom
= new Tom();
jerry
= new Jerry();

var handle = dojo.connect(tom, "greet", jerry, "hello"); //建立Tom和Jerry的连接
tom.greet(" Nice to meet you!"); //现在,tom接收了参数

 

 

事实上,能够自动传递参数的确非常方便,一个经典的例子就是当把一个函数与一个DOM事件(鼠标点击)连接起来时,通过传递事件对象可以让函数访问到事件目标、鼠标位置等重要信息,下面再用一个例子来说明这一点:

 

dojo.connect(dojo.byId("box"), /*页面中的一个dom元素*/
"onmouseover", /*鼠标事件*/
function (evt) {
alert(
"Event is:" + evt);
});

 


事件传播

有时候,也需要抑制浏览器对某些dom事件的内置处理,而不是一味地通过dojo.connect为这些任务提供自定义的处理程序,要抑制浏览器处理DOM事件的典型情况一般有2个,一是在用户单击超链接时,不让浏览器自动导航到相应地址;二是当用户按下回车键或单击提交按钮时候,避免浏览器自动提交表单。

 

 

 

要想在你的自定义处理程序执行完成后,阻止浏览器处理上述DOM事件,只要调用dojo.stopEvent或者DOMEvent的preventDefault方法,即可防止事件传播到浏览器。
其中,stopEvent方法之需要一个DOMEvent对象作为参数:

 

dojo.stopEvent(/*DOMEvent*/ evt)

 

注意:虽然可以阻止dojo.connect连接的个别DOM事件,但却无法再函数或JavaScript对象方法内部阻止由dojo.connect简历的事件链

 

 以下是一个使用stopEvent的示例:

 

dojo.stopEvent
//<a href="http://www.w3.org/" id="nav">W3ORG</a>
var nav = dojo.byId("nav"); //id为nav的锚链接元素
dojo.connect(nav, "onclick", function (evt) {
alert(
"anchor clicked");
dojo.stopEvent(evt);
//抑制浏览器导航并阻止事件冒泡
});

 


通过dojo.connect利用闭包
【通过dojo.connect利用闭包算是中高级的主题,要好好理解和体会。因为我可以负责的告诉你,这些知识迟早要派上大用场,因此要好这些侧地搞明白】

一次性连接

在某些情况下,我们需要建立一个连接,而当该连接涉及的函数被触发后就应该断开该连接。下面演示以下示例代码:

 

dojo.disconnect(handle);
/*
下面js代码中需要用到的html代码
<div id="box" style="border:solid 1px #ccc; height:100px; width:100px;"></div>
*/
var handle = dojo.connect(
dojo.byId(
"box"), //某个DIV元素
"onmouseover",
function (evt) {
//第一次鼠标移入div区域,会弹出对话框,然后再移入鼠标则无任何操作
alert("first mouseover + ");
dojo.disconnect(handle);
});

 

如果你熟悉闭包,那么第一反应可能是上面示例不可行,毕竟,变量handle是由对dojo.connect的调用返回的,但在作为参数传递给dojo.connect的函数内部确引用了该变量。为了说明这个示例的可行性,我们做了下面的分析。(仔细阅读

1,当dojo.connect执行时,尽管它接收了一个匿名函数作为参数,但是该匿名函数并没有被执行。

2,匿名函数内部的任何变量(如:handle)都将被绑定到其作用域链中,虽然这些变量存在于函数内部,但除非该函数执行,否则他们不会真正被引用。因此,也就不会出现引用错误。

3,由于dojo.connect在匿名函数执行以前返回handle变量,因此当匿名函数再执行时,该变量实际上已经存在了。因此可以直接传递给dojo.disconnect方法。

 

在循环中建立连接

另一种在开发中常见的情形是在循环体内建立连接,

 

/*建立连接*/
dojo.connect(/*Object | null*/ obj, /*String*/ event, /*Object | null*/ context, /*String | Function*/ method)   //返回一个句柄对象

 


dojo.disconnect

 

/*断开连接*/
dojo.disconnect(
/*Handle*/ handle);

/*断开连接*/
dojo.disconnect(/*Handle*/ handle);

 


dojo.forEach

迭代操作元素:
dojo的forEach方法会将每个数组元素作为参数传递给一个函数,并且不返回任何值。【提示:看到这个描述,大家会想到平常通过for循环来编写迭代数组元素的代码吧】 

//forEach方法签名
dojo.forEach(/*Array*/ array, /*Function*/ function) //不返回值

 

使用forEach方法最明显的好处是我们不用使用for循环中的计数器变量了,同时还可以把Array变量直接作为参数嵌入到调用中。最关键的是它利用作为第二个参数的函数提供的闭包保护了计数器变量的直接执行环境,以及引入循环快中的其他变量的持续性。并且forEach还可以接受一个可选的参数,该参数用于嵌入的函数提供环境。

为了让大家清楚forEach如何避免意外的发生,请看如下代码片段:

<ul>
<li>dojo</li>
<li>jquery</li>
<li>extjs</li>
<li>yui</li>
<li>mootools</li>
</ul>

 

常规的for循环:

代码
winodw.onload=function(){
var nodes = document.getElementsByTagName("li");
for(var i=0; i<nodes.length; i++)
{
nodes[i].onclick
= function(){
alert(
this.innerHTML + ":" + i); //你觉得这个i的值应该是多少?
}
}
}

 

上面的代码,你觉得这里的 i 的值应该是多少?由于匿名函数封闭的芷是词法变量i,而不是i的值,因此点击每个li节点都会得到最后的值。
 通过执行,可以发现,虽然每次点击li节点,能够alert出正规的innerHTML,但是 对于这个i值,并不是我们所预期的,而是每次都是5。

对此,forEach通过创建一个新的额词法作用域帮助我们解决了问题。以下经过修改的dojo.forEach代码片段展示了怎样才能既迭代数组,又能得到期望的值:

dojo.forEach
dojo.addOnLoad(//addOnLoad 等介于jquery的$(document).ready(function(){ /*code*/ })
function(){
var nodes = document.getElementsByTagName("li");
var x = 0; //初始化x
dojo.forEach(nodes, function(node, x){
node.onclick
= function(){
alert(
this.innerHTML + ":" + x);
}
});
}
)

 

这样就好了:)
//
//

posted @ 2010-06-13 10:29  sudo!!  阅读(3557)  评论(0编辑  收藏  举报