Dojo Query

on the Web In application development, the application of JavaScript is becoming more and more common and more complex. There are often hundreds of HTML elements in a Web page. It is possible to accurately and efficiently select the required elements and operate them. The program development stage saves coding time and reduces the probability of program errors. It can also improve the efficiency of the program during operation and provide a better user experience. Dojo provides a powerful Query function library, using an efficient query engine, can query the elements of the page according to element id, name, CSS, attributes and their combinations, and can handle the returned results very conveniently. This article introduces in detail the various query methods of dojo.query and the commonly used methods of processing the results. Appropriate use of dojo.query can greatly improve the efficiency of program development, reduce the amount of code, and improve coding quality.

Introduction to Dojo Query

In Web development, we often need to query and obtain references to certain HTML elements based on certain conditions, and then manipulate these elements to change Their properties, appearance, etc. In the standard JavaScript language, you can query the DOM tree through functions such as document.getElementById, document.getElementsByTagName, document.getElementsByClassName, and then traverse the returned results for processing. In some complicated situations, such as querying all elements whose class is “class1” and contains attribute “att1” under the div element whose id is “id1”, using standard JavaScript query appears to be inadequate, even if the code can be implemented. Numerous and lengthy. On the other hand, after getting the query result, if you want to set the class of all returned elements to “class2”, you need to traverse the result array and set them one by one. The query method using the JavaScript standard has limited functions, complex use, and performance degradation when there are too many page elements.

Dojo Query library provides a concise and powerful query syntax, as well as a convenient way of processing the returned results. The core of the Dojo Query library is a dojo.query function, which receives a query string and an optional DOM node as a parameter, and returns a NodeList object. On the one hand, we can accurately control the returned results by setting sophisticated query strings through id, element name, attributes, CSS, etc. and their combinations. On the other hand, the returned NodeList object provides a rich operation interface and calls many methods ( Such as addClass) can directly act on all elements, and supports chained calls. Such as the following example:

dojo.query(“div.someClassName”).style(“backgroundColor”,”gray” ).forEach(“item.disabled= true;”);

First query the DOM root node for class “someClassName “Div element, for all returned elements, set the style attribute “backgroundColor” to “gray”, and then set the element’s “disabled” attribute to “true”. Using Dojo Query can accurately control the query results and conveniently operate on all results. Its use is very simple, the code is refined, and the readability is strong. The following chapters of this article will introduce query syntax and common usage scenarios in detail, as well as the use of NodeList objects.

Query based on Dojo Query

When we use Dojo Query to query, the most commonly used query is based on the tag name of the HTML element, or the class and id attributes. These are also several ways we commonly use CSS elements to make selections. For example, suppose we want to query all the tags in the page, then we can write:

 dojo.query("img");

Here, img is the tag name. Since Dojo Query does not distinguish between upper and lower case, HTML elements like will also be included in the query results . The execution effect of the above grammar is equivalent to calling the DOM API in JavaScript: document.getElementsByTagName(“IMG”).

Similarly, the syntax for querying based on the class and id attributes is also very simple. For example, the following example is to query the element with id widget123 in the current page:

 dojo.query("#widget123");

Its execution effect is equivalent to calling document.getElementById(“widget123”); or, using the API provided by Dojo, it is equivalent to dojo.byId(“widget123”).

The query for the class attribute is equally simple and intuitive. For example, the following example is to find all the classes whose name is “offToSeeTheWij” HTML elements:

 dojo.query(".offToSeeTheWij");

Here we can see that the same function, if you use the DOM API Implementation, it will become very tedious:

Listing 1 DOM API query

 var list = []; var nodes = document.getElementsByTagName("*"); for( var x = 0; x 

< p style="margin-top:0px; margin-bottom:0px; padding-top:6px; padding-bottom:6px; border:0px; outline:0px; vertical-align:baseline; color:rgb(34,34, 34); line-height:1.5em; font-size:1.166em!important"> In the above code, we need to traverse all the nodes in the page, and determine whether the className attribute meets the matching conditions one by one. Through this typical example, we can clearly see that using Dojo Query has a very significant advantage in DOM node query.

In fact, using Dojo Query will not only have advantages in expressing the conciseness of query conditions, it is more advantageous than using it directly The way the DOM API performs processing is usually faster. Readers will see that this point becomes more prominent especially when we need to query relatively complex page node relationships.

In addition to what we have seen above, Dojo Query provides a basic query method based on tag, class, and id. It also provides many more complex query syntaxes, all of which follow the CSS specification. This approach of Dojo Query is very wise, because CSS is a Web technology that has been widely used and accepted by everyone. Its selective modification of HTML page elements has the characteristics of concise syntax and powerful functions. All current mainstream browsers have good support for CSS. Dojo Query follows the grammar of CSS, so that users can master the use of Dojo Query without learning a complete set of new query grammar to complete various complex query functions.

Currently, Dojo Query supports many common CSS selector syntax. For example, we can use a more complex class selector syntax to query the specified elements that meet the specified class attribute:

 dojo.query("div.someClassName");

For another example, we can combine each tag name to achieve a combined query. The following is to query all h1, h2, h3 nodes:

 dojo.query("h1,h2,h3");

In addition, Dojo Query also supports some special selectors defined in CSS 3. We can quote some special pseudo-class selectors in the query conditions. For example, we can use nth-child to query all odd-numbered child elements under the tbody node:

 dojo.query("tbody tr:nth-child(odd )");

Use first-child to query the first p child element under any node:< /p>

 dojo.query("p:first-child");

You can also use attribute selectors such as “^=”, “$=”, “*=” to implement queries that match specific string conditions. For example, the following codes are used to query the value of the name attribute starting with "item", ending with "item", and elements containing the word "item":

Listing 2 Using the attribute selector

 dojo.query("[name^=item]"); dojo.query("[name$=item]"); dojo.query(" [name*=item]");

The examples of Dojo Query we have seen above only accept one parameter, and they realize the query of nodes in the global scope, that is, the entire page scope. Dojo Query also supports relative queries within a local scope. At this point, in addition to the query expression, we need to pass in another parameter to indicate the root node of the query. The parameter can be a string, and Dojo Query will treat it as the id value of the element; or we can also pass in a DOM node. In the following example, we have implemented the query function under thisForm node:

Listing 3 Relative queries within a local scope< /h3>

       

Follow-up operations on query results

Through the introduction of the above chapters, we know that the result returned by Dojo Query is a NodeList object. NodeList is an extended Array object, which provides a wealth of operation interface methods. Basically, NodeList provides almost all methods of manipulating the DOM, and it is simple and easy to use; because it is an Array object, it supports all Dojo operations on arrays; at the same time, it also provides many methods to directly handle events . Moreover, NodeList also has a significant advantage, that is, many methods support chained calls. The so-called chain call means that the NodeList method will still return the current object after the method is called. You can continue to apply other operations through the "." cascade, for example:

List 4 chain call

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

Let’s Let's take a look at the basic operation of NodeList.

As an Array object, NodeList has a length property, and it can be manipulated by methods such as at, forEache, push, and pop. It should be noted that at, map, forEach, slice, splice, contact, etc. can all be chained calls, but push, pop, shift and unshift are not. Please refer to the following code snippet:

List 5 NodeList basic operation

 var l = dojo.query(".thinger"); console .log("Size of items with class thinger:"+l.length); //Add objects to NodeList l.push(dojo.create('div', {innerHTML:'hi' })); console.log( "Size of items with class thinger:" + l.length); l.push(dojo.byId("foo")); console.log("Size of items with class thinger:" + l.length); // query the id of foo The position of the element in the NodeList console.log( l.indexOf(dojo.byId("foo")) ); // Get the fourth element var node = l[3]; // Use the at method to find the first one at a time For the second and fourth elements, the returned result is also a NodeList. var newList = l.at(1, 3);

In the process of using NodeList, if everyone If you are careful, you may notice that while encapsulating the dojo.* method, NodeList omits the first parameter through a self-descriptive method, which is also in line with our usage habits. For example, NodeList's forEach method, its usage looks a lot like dojo.forEach(Array, Function). But the first parameter is implicitly provided, that is, the NodeList object itself is used as the value of the Array parameter.

The following example shows how to add content within a

.

Listing 6 NodeList.forEach method

 dojo.query("div").forEach(function(node, index, array) {node.innerHTML = "new version content!"; });

Note, The concept of omitting the first parameter through self-description runs through all NodeList APIs. The common methods of NodeList described below also apply the above concepts.

dojo.style is an API applied to a single node. This API corresponds to the NodeList API, which is equivalent to implicit province Go to the node parameter and apply it to every element in the NodeList.

Listing 7 NodeList.style method

// Perform query

 var borders = dojo.query(".thinger").style("border"); // Set a new value dojo.query(".thinger").style("border" , "1px solid black"); // delete and add class dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger"). addClass("thinger2") ;

The above code snippet is an example of NodeList API for cascading calls. Most of the DOM-related objects return to the NodeList as in the above example, and then we can make chain calls to it. But not all APIs can be cascaded, NodeList.coords is an exception, it returns a set of eligible coordinate values, so it cannot be cascaded.

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象{ x,y,z}

所以我们在使用NodeList API 时,要注意并不是所有的API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

在 Web 应用程序开发中,JavaScript 的应用越来越普遍,越来越复杂,一个 Web 页面中往往有成百上千个 HTML 元素,准确、高效地选择所需的元素并对其进行操作,不仅可以在程序开发阶段节省编码时间,降低程序出错的概率,在运行还能提高程序运行效率,提供更好的用户体验。 Dojo 提供了功能强大的 Query 函数库,使用一个高效的查询引擎,能够根据元素 id,名称,CSS,属性及其组合对页面的元素进行查询,并且能够对返回结果进行非常方便的处理。本文详细介绍 dojo.query 的各种查询方式,以及常用对结果的处理方式。恰当使用 dojo.query 能够大大提高程序开发的效率,减少代码量,提高编码质量。

在 Web 应用程序开发中,JavaScript 的应用越来越普遍,越来越复杂,一个 Web 页面中往往有成百上千个 HTML 元素,准确、高效地选择所需的元素并对其进行操作,不仅可以在程序开发阶段节省编码时间,降低程序出错的概率,在运行还能提高程序运行效率,提供更好的用户体验。 Dojo 提供了功能强大的 Query 函数库,使用一个高效的查询引擎,能够根据元素 id,名称,CSS,属性及其组合对页面的元素进行查询,并且能够对返回结果进行非常方便的处理。本文详细介绍 dojo.query 的各种查询方式,以及常用对结果的处理方式。恰当使用 dojo.query 能够大大提高程序开发的效率,减少代码量,提高编码质量。

在 Web 应用程序开发中,JavaScript 的应用越来越普遍,越来越复杂,一个 Web 页面中往往有成百上千个 HTML 元素,准确、高效地选择所需的元素并对其进行操作,不仅可以在程序开发阶段节省编码时间,降低程序出错的概率,在运行还能提高程序运行效率,提供更好的用户体验。 Dojo 提供了功能强大的 Query 函数库,使用一个高效的查询引擎,能够根据元素 id,名称,CSS,属性及其组合对页面的元素进行查询,并且能够对返回结果进行非常方便的处理。本文详细介绍 dojo.query 的各种查询方式,以及常用对结果的处理方式。恰当使用 dojo.query 能够大大提高程序开发的效率,减少代码量,提高编码质量。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.length; x++){   if(nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with cl ass thinger:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < n odes.length; x++){   if(nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class thinger:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.len gth; x++){   if(nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class th inger:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.length; x++ ){   if(nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class thing er:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.length; x++){   if( nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class thinger: " + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 Jav aScript 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

Dojo Query 简介

在 Web 开发中,我们经常需要根据一定的条件查询并获取某些 HTML 元素的引用,然后对这些元素进行操作以改变它们的属性、外观等。在标准的 JavaScript 语言中,可以通过 document.getElementById, document.getElementsByTagName, document.getElementsByClassName 等函数对 DOM 树进行查询,然后遍历返回结果分别进行处理。在一些复杂的情况下,比如要查询 id 为“id1”的 div 元素下所有 class 为“class1”并且包含属性“att1”的元素,使用标准的 JavaScript 查询就显得力不从心,即使能够实现其代码也显得纷繁冗长。另一方面,在得到查询结果以后,假如要将所有返回的元素的 class 设置为“class2”,则需要遍历结果数组,一一进行设置。使用 JavaScript 标准的查询方式功能有限,使用复杂,而且当页面元素过多时还会导致性能降低。

Dojo Query 库提供了一种简洁而强大的查询语法,以及对返回结果简便的处理方式。 Dojo Query 库的核心是一个 dojo.query 函数,该函数接收一个查询字符串,以及一个可选的 DOM 节点作为参数,返回一个 NodeList 对象。一方面我们可以通过 id,元素名称,属性,CSS 等及其组合设置精巧的查询字符串准确控制返回的结果,另一方面返回的 NodeList 对象提供了丰富的操作接口,对其调用的很多方法(如 addClass)都可以直接作用于所有的元素,并且支持链式调用。如下面的例子:

dojo.query("div.someClassName").style("backgroundColor","gray").forEach("item.disabled= true;");

首先在 DOM 根结点查询 class 为“someClassName”的 div 元素,对于返回的所有元素,将 style 属性“backgroundColor”设置为“gray”,然后将元素的“disabled”属性设置为“true”。使用 Dojo Query 能够精确地控制查询结果并方便地对所有结果进行操作,其使用非常简便,代码精炼,可读性强。本文后续章节将详细介绍查询语法及常见使用场景,以及 NodeList 对象的使用。

基于 Dojo Query 的查询

当我们采用 Dojo Query 进行查询时,通常使用最多的就是根据 HTML 元素的 tag 名称,或是 class 以及 id 属性来进行查询。这也是我们在引用 CSS 元素进行选择时常用的几种方式。比如,假设我们想查询页面内所有的 标签,那么不妨可以这么写:

 dojo.query("img");

此处,img 即为标签名称,由于 Dojo Query 并不对大小写进行区分,故而形如 这样的 HTML 元素也会被包含在查询结果之中。上述语法的执行效果,等价于在 JavaScript 中调用 DOM API:document.getElementsByTagName("IMG")。

同样,根据 class 和 id 属性进行查询的语法,也很简单。例如,下面的例子实现的是在当前页面中查询 id 为 widget123 的元素:

 dojo.query("#widget123");

其执行效果,等价于调用 document.getElementById("widget123");,或者,利用 Dojo 提供的 API,则相当于 dojo.byId("widget123")。

针对 class 属性的查询同样的简单直观,例如,下面的例子是查找所有 class 名称为“offToSeeTheWij”的 HTML 元素:

 dojo.query(".offToSeeTheWij");

此处我们可以看到,同样的功能,如果用 DOM API 来实现,则将会变得非常冗长乏味:

清单 1 DOM API 查询

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.length; x++){   if(nodes[x] .className == "progressIndicator"){     list.push(nodes[x]);   }  }

在上述代码中,我们需要对页面中的所有节点进行遍历,逐一判断其 className 属性是否满足匹配条件才行。通过这个典型的例子,我们可以清楚地看到,使用 Dojo Query 在 DOM 节点查询方面具有非常显著的优势。

事实上,使用 Dojo Query 不仅在表达查询条件的简洁性上会更具优势,相比于直接利用 DOM API 进行处理的方式,其执行速度也通常会更加的快。接下来读者便会看到,这一点尤其在我们需要查询相对复杂的页面节点关系时,会变得更为突出。

除了上述我们看到的,Dojo Query 提供了基本的依据 tag、class、id 进行查询的方式以外,它还提供了许多更为复杂的查询语法,而所有这些语法则都遵循于 CSS 规范。 Dojo Query 的这一做法十分的明智,因为 CSS 是已经被大家所广泛使用和接受了的一种 Web 技术,其对 HTML 页面元素进行选择性修饰的方式,兼具语法简洁和功能强大的特点,目前所有的主流浏览器都对 CSS 有很好的支持。 Dojo Query 沿用了 CSS 的语法,使得使用者无需学习一整套新的查询语法,便可以很好的掌握 Dojo Query 的使用,以完成各种复杂的查询功能。

目前,Dojo Query 支持许多常见的 CSS 选择器语法。例如,我们可以利用较为复杂的类选择器语法,实现对符合指定 class 属性的指定元素进行查询:

 dojo.query("div.someClassName");

又比如,我们可以将对各 tag 名称结合起来实现组合查询,下面完成的即是查询出所有的 h1,h2,h3 节点:

 dojo.query("h1,h2,h3");

除此以外,Dojo Query 还支持某些特殊的在 CSS 3 中定义的选择器。我们可以在查询条件中引用某些特殊的伪类选择符,比如可以利用 nth-child 来查询 tbody 节点下所有奇数序号的子元素:

 dojo.query("tbody tr:nth-child(odd)");

利用 first-child 来查询任意节点下的首个 p 子元素:

 dojo.query("p:first-child");

还可以利用诸如“^=”、“$=”、“*=”这样的属性选择器,实现匹配特定字符串条件的查询。例如,下列代码就是分别用来查询 name 属性的取值以“item”打头,以“item”结尾,和包含“item”字样的元素的:

清单 2 使用属性选择器

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

上面我们看到的有关于 Dojo Query 的例子都只接受一个参数,它们实现的是在全局范围内,即整个页面范围内,对节点进行查询。 Dojo Query 还支持局部范围内的相对查询。此时,除了查询表达式外,我们需要传入另一个参数,用以指示查询起始的根节点。该参数可以是一个字符串,Dojo Query 会将其视作元素的 id 值;或者我们也可以传入一个 DOM 节点。在下面的例子里,我们实现了在 thisForm 这个节点下进行查询的功能:

清单 3 局部范围内的相对查询

               

对查询结果进行后续操作

通过以上章节的介绍,我们知道,Dojo Query 返回的结果是 NodeList 对象。 NodeList 是一个扩展的 Array 对象,它提供了丰富的操作接口方法。基本而言,NodeList 提供了几乎所有操作 DOM 的方法,且简单易用;因为它本身是 Array 对象,所以它支持所有的 Dojo 对数组的操作方法;同时,它也提供了很多直接处理事件的方法。而且,NodeList 还有一个显著的优点,就是很多方法支持链式调用。所谓链式调用,是指 NodeList 的方法调用之后仍会返回当前的对象,可以通过“.”级联继续应用其他的操作,例如 :

清单 4 链式调用

dojo.query(".thinger ").style {border :"1px" }).removeClass("thinger").addClass("thinger2");

我们先来看一下 NodeList 的基本操作。

作为 Array 对象,NodeList 具有长度属性,而且可以通过 at,forEache,push,pop 这些方法来操纵它。需要注意的是 at,map,forEach,slice,splice,contact 等都可以进行链式调用,但是 push,pop,shift 和 unshift 则是不可以的。请参看以下代码片段:

清单 5 NodeList 基本操作

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class thinger:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

在使用 NodeList 的过程中,如果大家细心的话,也许会注意到,NodeList 在封装 dojo.* 方法的同时,通过自描述(self-descriptive)方式省略掉了第一个参数,这也是符合我们使用习惯的。例如 NodeList 的 forEach 方法,其用法看起来很像 dojo.forEach(Array,Function)。不过第一个参数被隐式提供了,即将 NodeList 对象本身作为 Array 参数的值。

下面这个例子说明如何在

内添加内容。

清单 6 NodeList.forEach 方法

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

注意,通过自描述省略第一个参数的概念贯穿所有 NodeList 的 API。下面介绍的 NodeList 的常用方法,也适用上面的概念。

dojo.style 是一个应用于单个 node 的 API,这个 API 对应到 NodeList API,等价于隐式省去 node 参数,然后将其应用于 NodeList 中的每一个元素。

清单 7 NodeList.style 方法

// 执行查询

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

上面的代码段是一个 NodeList API 进行级联调用的例子。大部分 DOM 相关的对象都如上述例子一样,返回 NodeList,然后我们可以对其进行链式调用。但并非所有的 API 都可以级联,NodeList.coords 就是一个例外,它返回的是一组符合条件的坐标值,因此是不可以级联的。

var nl = dojo.query(".foo");

var coords = nl.coords(); // 例如返回对象 { x,y,z}

所以我们在使用 NodeList API 时,要注意并不是所有的 API 都可以级联调用。这个基本上可以通过是否返回 NodeList 对象进行判断。

现在,我们简单介绍一下,如何在 NodeList 中使用事件。

以 NodeList.connect() 为例,它提供了一种方法,可以向所有的 DOM Node 中添加事件。对应于 dojo.connect API,同样,node 参数对应于 NodeList 中当前的 Node。

清单 8 NodeList.connect 方法

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

我们可以像使用 dojo.connect 一样,向 NodeList.connect 添加任何事件。为了方便起见,NodeList 提供了很多 DOM 的直接操纵对应事件的方法,所以上面的例子也可以写成:

清单 9 NodeList.onclick 方法

 dojo.query("input").onclick(function(e){   alert("new event!");  });

直接支持的事件还包括 onclick, onmouseenter, onmouseleave, onmouseover, omouseout, ondblclick 等。例如:

清单 10 NodeList 的鼠标事件

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

有时,为了自己使用的方便或者出于兴趣,也许你想在 NodeList 中增加一些方法,以便可以在 dojo.query 的调用中使用这些方法。为此我们可以通过 dojo.extend 扩展 NodeList 向 NodeList 中添加自定义的新方法。如:

清单 11 扩展 NodeList 方法

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.style({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

注意,此处的关键是“return this”,它保证了后续链式调用的成功执行。

另外,为了保持 NodeList 基本操作函数的紧凑性,一些扩展方法是以外部包的形式提供的。例如,dojo.NodeList-fx 为 NodeList 引入了 FX/Animation,dojox.fx.ext-dojo.NodeList 提供 dojox.fx 里关于动画的功能,dojo.NodeList-html 提供了高级的 HTML 操纵功能,等等。

通过以上的介绍我们可以看出,通过 NodeList,我们可以更加简单地操纵 DOM 对象,更加方便地添加事件,更重要的是,我们还可以进行链式操作,以减少代码复杂度,加快开发速度。

小结

现在很多主流的 JavaScri pt 框架都提供了功能强大的 Dom 节点查询和操纵功能,使用这些框架的好处也是显而易见的,可以屏蔽浏览器差异,能够更为精确简便地查找节点,并提供对结果的集合操作来简化后续处理等等。本文介绍了 Dojo Query 的查询语法和使用场景以及如何对结果集合 NodeList 进行处理,在 Dojo 开发中恰当使用 Dojo Query 能够大大提高代码质量,缩短开发周期。

 dojo.query("img");

 dojo.query("#widget123");

 dojo.query(".offToSeeTheWij");

 var list = [];  var nodes = document.getElementsByTagName("*");  for(var x = 0; x < nodes.length; x++){   if(nodes[x].className == "progressIndicator"){     list.push(nodes[x]);   }  }

 dojo.query("div.someClassName");

 dojo.query("h1,h2,h3");

 dojo.query("tbody tr:nth-child(odd)");

 dojo.query("p:first-child");

 dojo.query("[name^=item]");  dojo.query("[name$=item]");  dojo.query("[name*=item]");

               

 var l = dojo.query(".thinger");  console.log("Size of items with class thinger:"+l.length);  //NodeList 中加入对象 l.push(dojo.create('div', { innerHTML:'hi' }));  console.log("Size of items with class thinger:" + l.length);  l.push(dojo.byId("foo"));  console.log("Size of items with class thinger:" + l.length);  // 查询 id 为 foo 的元素在 NodeList 中的位置 console.log( l.indexOf(dojo.byId("foo")) );  // 获取第四个元素 var node = l[3];  // 通过 at 方法,一次找出第二和第四个元素,返回结果也是一个 NodeList。 var newList = l.at(1, 3);

 dojo.query("div").forEach(function(node, index, array){     node.innerHTML = "new version content!";  });

 var borders = dojo.query(".thinger").style("border");  // 设置新值 dojo.query(".thinger").style("border", "1px solid black");  // 删除,添加 class  dojo.query(".thinger").style({border :" 3px solid red" }).removeClass("thinger").  addClass("thinger2");

 dojo.query("input").connect("onclick", function(e){   alert("new event!");  });

 dojo.query("input").onclick(function(e){   alert("ne w event!");  });

 dojo.query("p").onmouseenter(function(e){     dojo.style(e.target, "color", "red");  }).onmouseleave(function(e){     dojo.style(e.target, "color", "blue");  });

 dojo.extend(dojo.NodeList, {   setColor: function(newColor){      this.styl e({        color: newColor      });      return this;   }  });  dojo.query("p").setColor ("yellow");

Leave a Comment

Your email address will not be published.