入门
对DOM编程的一个关键要素是能够快速高效的获取到你所要使用的节点。之前我们曾经介绍过使用dojo.byId来查找DOM节点的方法。但是,这种方法的局限性也很明显。你很难为页面上每个节点都起一个唯一的ID;而且通过dojo.byId查找得到的总是单个节点,当你需要对一组节点做同样的动作时,dojo.byId 就无能为力了。解决这些局限的方法就是我们今天将要介绍的:dojo.query 。 dojo.query 方法使用了类似CSS查询的方式来获取一组节点,在新版的dojo当中,它甚至已经完全可以支持高级的CSS3 选择器(selector )了。
常用查询
为了演示一些最常用的DOM查询示例,我们假设了如下的一个HTML页面片段. (这是常见的包含一系列链接的HTML片段)
view plain
- <ul id="list">
- <li class="odd">
- <div class="bold">
- <a class="odd">Odd</a>
- </div>
- </li>
- <li class="even">
- <div class="italic">
- <a class="even">Even</a>
- </div>
- </li>
- <li class="odd">
- <a class="odd">Odd</a>
- </li>
- <li class="even">
- <div class="bold">
- <a class="even">Even</a>
- </div>
- </li>
- <li class="odd">
- <div class="italic">
- <a class="odd">Odd</a>
- </div>
- </li>
- <li class="even">
- <a class="even">Even</a>
- </li>
- </ul>
- <ul id="list2">
- <li class="odd">Odd<li>
- </ul>
针对上述的HTML片段,能想到的第一个操作通常是如何获取到整个列表的一个句柄. 当然你可以用dojo.byId, 但dojo.query 也可以达到同样目的. 虽然初看起来,你会觉得在这里dojo.query 不是那么方便,但结合后面的例子你就会发现它的好处。
view plain
- // 获取所有包含节点ID为"list"节点的数组
- var list = dojo.query("#list")[0];
通过在参数中加入"#", 我们告诉dojo.query 去查找节点的"ID"属性。这是从CSS操作中借鉴来的语法。 需要注意的是dojo.query 的返回值永远是一个数组。 在这个例子中,因为只有一个ID叫"list" 的节点,所以我们直接取出了该数组的第一个元素。
上面我们看到了如何通过ID来获取节点,dojo.query 可不是只有这么点能力。它还支持通过class name来选择节点。假设我们希望能够获取所有class name等于"odd" 的节点:
view plain- // retrieve an array of nodes with the class name "odd"
- var odds = dojo.query(".odd");
通过在参数中加入".", 我们告诉dojo.query 现在是要匹配节点的className属性, 这也是借鉴了CSS的语法。在这个例子中,dojo.query 将会返回包含4个<li> 节点和3个<a>节点 的数组。
限定查询条件的作用域
你可能已经发现了,在上一个例子中的得到odds数组同时包含了来自两个列表的节点。 假设我们只需要获取第一个列表中的odd节点呢? 有两种方法
view plain- // 使用选择器来限定查询的作用域
- var odds1 = dojo.query("#list .odd");
- // 使用第二个参数来限定查询的作用域
- var odds2 = dojo.query(".odd", dojo.byId("list"));
这两种方法返回的都是相同的元素, 第一个方法使用了选择器的语法,限制了查询的结果在ID为list的元素内,而第二个方法则将一个节点作为限定查询参数传入query。
当dojo.query 方法不包含第二个参数时,它会搜索整个DOM树结构,遍历<HTML>标签中包含的每个节点。 如果该方法调用时包含了一个DOM节点作为第二参数,这个节点就是查询的作用域, 查询的结果一定是该节点或其子节点。
如果你的页面的DOM树结构比较小,比如像我们这里使用的列表的例子,那么省略第二参数的做法是可以的,也不会过分影响效率。但是如果页面很复杂,那么强烈建议你在使用dojo.query是明确指定第二参数来限定查询的作用域.这会大大减少无谓的对整个页面进行搜索操作从而提升速度和性能。
为了方便,在接下来的例子中,我们都会省略第二个参数,不过请你一定要记住:在真实应用中你应该尽可能的制定作用域来让查询操作快速高效。
更多高级的查询
前面的例子中,我们查询得到的结果集包含了<li> 节点和<a> 节点两种,如果我们只想要其中的<a> 节点该如何做呢? 在dojo.query 查询时我们可以将 标签名和class 名组合作为查询条件:
view plain
- var oddA = dojo.query("a.odd");
dojo.query 还支持另一种选择器, ">". CSS中使用">" 并不被所有浏览器支持,但是在dojo.query中却可以通用.
使用这个选择器,查询会
view plain- // 获取任意一个有li节点作为其父节点的a节点
- var allA = dojo.query("li a");
- // 获取任意一个有li节点作为其直接父节点的a节点
- var someA = dojo.query("li > a");
查看示例
在我们的例子中,allA会查询出6个<a>节点,而someA只会包含2个<a>。 在">" 两侧可以使用任意的其他选择器,包括class 选择器。这里我们只是介绍了几种最常用的选择器,但dojo.query 是完全兼容CSS3的,还能够支持很多其他的选择器 .你可以自己进一步学习掌握.
NodeList(操作dojo.query返回的结果集)
前文提到,dojo.query 返回的是匹配查询结果的所有节点构成的数组;这个数组实际上是一个特殊的数组对象称为dojo.NodeList, 该数组对象内建了一系列可以方便操作其中节点的方法.
下面我们来看一下其中常用的一些方法, 在这个章节我们会使用下面的一个HTML代码片段:
view plain- <div id="list">
- <div class="odd">One</div>
- <div class="even">Two</div>
- <div class="odd">Three</div>
- <div class="even">Four</div>
- <div class="odd">Five</div>
- <div class="even">Six</div>
- </div>
dojo.NodeList 内建了一些Dojo 数组辅助方法. 例如forEach, 它可以对数组中的每个元素执行一个函数:
view plain- dojo.query(".odd").forEach(function(node, index, nodelist){
- // 针对query返回的数组中的每个节点,执行本方法
- dojo.addClass(node, "red");
- });
被传入forEach的函数是一个回调函数,该回调函数支持3个参数: 当前正在操作的节点,该节点在数组中的位置序号,以及当前正在遍历的结果集(是一个dojo.NodeList 对象)
对大多数开发者而言,第三个参数一般用不到, 仅当你在回调函数中需要去操作结果集中的其他节点时需要用到这个参数. forEach方法还可以接受第二个参数,作为回调函数调用的作用域(Scope)
dojo.NodeList中内建的其他数组辅助方法还包括: map,filter,every, 和 some. 大多方法都返回一个dojo.NodeList 对象,因此很容易串联使用. some 和every是例外,它们返回值是布尔值(boolean)
dojo.NodeList还内建了一些方面DOM操作的方法, 上一个例子还可以进一步简化为
view plain
- // 向所有符合".odd" 查询条件的节点加入className属性 "red"
- dojo.query(".odd").addClass("red");
- // 向所有符合".even" 查询条件的节点加入className属性 "blue"
- dojo.query(".even").addClass("blue");
这些DOM操作方法会在dojo.NodeList中每个节点上执行,同事返回值仍然是一个dojo.NodeList,可以串联使用.例如
view plain
- //所有符合".odd"条件的节点上删掉red属性而添加blue属性
- dojo.query(".odd").removeClass("red").addClass("blue");
其他dojo.NodeList 的DOM操作方法还包括: style, toggleClass,replaceClass, place 和empty. 所有这些方法都会返回dojo.NodeList,供串联使用.
view plain
- //把所有符合".even"条件的节点的字体颜色变为"while",并在节点上添加className "italic"
- dojo.query(".even").style("color", "white").addClass("italic");
事件
dojo.NodeList 上提供的另一重要方法是connect,用来连接DOM事件. 关于如何在Dojo中处理DOM事件会在下一个讲义中详细讨论,我们这里要解释一下如何使用dojo.NodeList的connect方法.
特别要注意的是,虽然在dojo.NodeList上使用connect很方便,但是并不适用于dojo.NodeList 包含大量节点的情形, 这种情况下应该使用一种称为事件代理(event delegation)的技巧.关于这一技巧我们会在未来的讲义中探讨.
view plain
- <button class="hookUp demoBtn">Click Me!</button>
- <button class="hookUpToo demoBtn">Click Me!</button>
- <button class="hookUpToo demoBtn">Click Me!</button>
- <button class="hookUp demoBtn">Click Me!</button>
- <mce:script type="text/javascript"><!--
- // 等待浏览器中DOM树完全加载完毕再执行操作
- dojo.ready(function(){
- // 连接到所有符合".hookUp"条件的节点的 "onclick" 事件
- dojo.query(".hookUp").connect("onclick", function(){
- alert("This button is hooked up!");
- });
- // 另一种连接事件的语法
- dojo.query(".hookUpToo").onclick(function(){
- alert("This button is hooked up too!");
- });
- });
- // --></mce:script>
上面的例子中我们演示了两种将dojo.NodeList连接到DOM事件的方法:
通用的connect 方法, 参数中指定事件名称和回调函数
使用一系列预定义的onXXXX 方法, 完整的方法列表 可以在参考手册中查找
第二种做法更加简洁一些,但是内建的onXXX方法仅包含了标准的DOM事件, 对于一些非标准事件例如 DOMAttrModified, 则只能使用第一种方法.
小结
利用dojo.query 以及dojo.NodeList,对批量的DOM节点进行操作是很简单的:
使用dojo.query 查询到你所需要操作的节点,再使用dojo.NodeList的内建方法对这些节点进行修改操作. 下面一章我们将会进一步介绍如何使用Dojo向页面中添加互动,如何使用dojo中的事件机制。