5.4 在HTML文档中查找元素
在HTML文档中与在XML文档中查找元素通常情况下还是有很大不同的。现代HTML事实上是XML的一个子集,这么说来似乎有点自相矛盾。但HTML文档包含了许多本质的不同,而这正是你可以充分利用的地方。
对JavaScript/HTML开发者来说,最重要的两个优势是利用类和CSS选择器(selector)。将这熟记于心,你就可创建一系列强大的函数,使DOM的操作更简单、更易理解。
5.4.1 通过类的值查找元素
通过类名字定位元素是一种很普遍的技术,2003年由Simon Willison(http://simon.incutio. com)普及,而原创的则是Andrew Hayward(http://www.mooncalf.me.uk)。该技术直截了当:遍历所有的元素(或者某元素的所有后代元素)直至找到指定类的元素。一种可能的实现方法如代码清单5-14所示。
代码清单5-14 找出全部有指定类值的元素的函数
function hasClass(name,type) {
var r = [];
// 定位到类值上(允许多类值)
var re = new RegExp("(^|\\s)" + name +
"(\\s|$)");
// 限制类型的查找,或者遍历所有元素
var e = document.getElementsByTagName(type || "*");
for ( var j = 0; j < e.length; j++ )
// 如果元素拥有指定类,把它添加到函数的返回值中
if ( re.test(e[j]) ) r.push( e[j] );
// 返回符合的元素列表
return r;
}
现在你就可使用这个函数来在任意的或者指定类型(比如<li>或<p>)的元素中迅速查找有指定类值的元素。指定标签名会比遍历所有元素(*)的查找更快,因为需检索的目标更少。比如,在我们的HTML文档中,如果需要找出类值有test的所有元素你可以这么做:
hasClass("test")
如果只需找类值有test的<li>元素,则可以这么做:
hasClass("test","li")
当然,如果只需找出类值有test的<li>元素的第一个,则可以这么做:
hasClass("test","li")[0]
这个函数本身就已非常强大,一旦结合getElementById和getElementsByTagName,你就可以创建更强大的工具集合解决大部分棘手的DOM问题了。
5.4.2 使用CSS选择器查找元素
作为一个Web开发者,你应该已经知道选择HTML元素的一种方案:CSS选择器。CSS选择器是用于赋予元素样式的表达式。随着CSS标准(1、2和3)的每次修订,选择器规范增加了越来越多的重要特点,由此开发者更容易精确定位到所需的元素。不幸的是,浏览器对CSS 2和CSS 3的实现慢得不可思议,所以你或许并不知道CSS中某些新奇酷的特点。如果你对这些感兴趣,建议你浏览W3C的这些主题页面:
l CSS 1选择器:http://www.w3.org/TR/REC-CSS1#basic-concepts。
l CSS 2选择器:http://www.w3.org/TR/REC-CSS2/selector.html。
l CSS 3选择器:http://www.w3.org/TR/2005/WD-css3-selectors-20051215/。
每种选择器规范可用的特点基本上都差不多,每一个后续的版本也都包含前一版的所有特点,同时,每次新版都会增加一系列的新特点。举个例子,CSS 2包含了特性(attribute)选择器和子选择器,而CSS 3则提供了额外的语言支持、性质类型选择以及逻辑非等。比如,以下所有这些都是正确的CSS选择器。
l #main<div>p:该表达式查找一个id为main的元素下所有的<div>元素,然后是这些元素下所有的<p>元素。它们都是CSS 1下的选择器。
l div.items>p:该表达式查找所有的类值为items的<div>元素,然后定位到所有的子<p>元素。这是正确的CSS 2选择器。
l div:not(.items):这则定位到所有没有值为items的类的所有<div>元素。这是正确的CSS 3选择器。
现在,你可能会觉得疑惑,实践中如果并不能使用它们来定位元素(而只能赋予样式)的话,为何会在此讨论CSS选择器。实际上很多前卫的开发者已经涉足开发能兼容CSS1甚至完全支持CSS 3的选择器的实现。使用这些库能让你顺利、方便地选择任意元素并对它们进行操作。
1.cssQuery
第一个完全支持CSS 1-3可用的公开库叫cssQuery,由Dean Edwards(http://dean.edwards.name)创立。其背后的动机很简单:你提供CSS选择器,cssQuery帮你找出所有匹配的元素。此外,cssQuery可以分解成多个子库,有一个是每个CSS选择器“管理员”,它甚至能执行CSS 3的语法,如果有必要的话。这个独特的库非常复杂但能运行在所有现代浏览器上(Dean是一位跨浏览器支持的忠实拥趸)。要应用整个库,你需要提供选择器,也可选择加入上下文元素以便加快搜索。以下是例子:
// 查找所有<div>元素的子<p>元素
cssQuery("div > p");
// 查找所有的<div>, <p>和<form>元素
cssQuery("div,p,form");
// 查找所有<p>元素和<div>元素,然后查找在这些元素内的a元素
var p = cssQuery("p,div");
cssQuery("a",p);
执行cssQuery函数会返回匹配的元素列表。由此你就可以像使用getElementsByTagName一样对元素进行操作。比如,为所有指向Google的链接增加边框,可按如下方法做:
// 为所有指向Google的链接增加边框
var g = cssQuery("a[href^='google.com']");
for ( var i = 0; i < g.length; i++ ) {
g[i].style.border = "1px dashed red";
}
关于cssQuery的更多信息可以在Dean Edwards的网站上获取,那里同时也提供完整的源码下载:http://dean.edwards.name/my/cssQuery/.
提示 Dean Edwards是一位JavaScript魔术师,他的代码令人惊讶。强烈建议你稍加浏览他的cssQuery库,看看他的JavaScript扩展性写得多么好。
2.jQuery
尽管jQuery是JavaScript库世界的新成员,但它提供了一些新颖而引人注目的JavaScript编程方式。我最初只想把jQuery写成一个“简便”的CSS选择器库,类似于cssQuery,直到Dean Edwards发布了卓越的cssQuery库,迫使我开辟了另一个不同的方向。这个库提供了完整的CSS 1-3的支持,同时也有基本的XPath支持。在此之上,它还提供了进一步定位和操作DOM的能力。跟cssQuery一样,jQuery完全支持现代浏览器。以下是使用混合CSS和XPath的jQuery自定义方式来选择元素的例子:
// 查找所有类值为'links', 同时内有p元素的<div>元素
$("div.links[p]")
// 查找所有<p>, <div>元素的所有子孙元素
$("p,div").find("*")
// 查找指向Google的所有偶数链接
$("a[@href^='google.com']:even")
现在,如需进一步使用jQuery元素选择返回的结果,你有两种方式。第一,你可以运行$("expression").get()来获取匹配元素的列表——这跟cssQuery完全一致。第二,你可以使用jQuery内置的特殊函数来操作CSS和DOM。所以,回到使用cssQuery为指向Google的链接加边框的例子上,你现在就可以这么做:
// 为所有指向Google的链接增加边框
$("a[@href^=google.com]").css("border","1px dashed red");
你可以从jQuery项目网站上找到大量的例子、演示和文档等,此外,还有可定制的下载:http://jquery.com/。
注意 应该指出的是,cssQuery或jQuery实际上并不只是能定位HTML文档而已,它们可以在任何XML文档上使用。至于纯粹的XML形式定位,请继续阅读下面的XPath部分。
5.4.3 XPath
XPath表达式是一种不可思议的定位XML文档的强大的方式。自问世几年来,几乎可以肯定DOM实现的背后必有XPath。尽管相对冗长,但XPath表达式比起CSS选择器来,能做的事情更多也更强大。表5-1并行比较了一些CSS选择器和XPath表达式的某些不同。
表5-1 CSS 3选择器与XPath表达式的比较
|
目 标 |
CSS 3 |
XPath |
|
所有元素 |
* |
//* |
|
所有<p>元素 |
p |
//p |
|
所有子元素 |
p> * |
//p/* |
|
由ID获取元素 |
#foo |
//*[@id='foo'] |
|
由类获取元素 |
.foo |
//*[contains(@class, 'foo')] |
|
由特性(attribute)获取元素 |
*[title] |
//*[@title] |
|
<p>的第一个子元素 |
p >*:first-child |
//p/*[0] |
|
所有拥有子元素的<p> |
不支持 |
//p[a] |
|
下一个元素 |
p + * |
//p/下一兄弟元素::*[0] |
如果前面这些表达式激发了你的兴趣,推荐你浏览XPath的两个规范(尽管现代浏览器通常只完全支持XPath 1.0)以初步了解它们是如何工作的:
l XPath 1.0:http://www.w3.org/TR/xpath/。
l XPath 2.0:http://www.w3.org/TR/xpath20/。
如果需要深入这个主题,我推荐你阅读O’Reilly出版的由Elliotte Harold和Scott Means所著的XML in a Nutshell(2004),或者Apress出版的由Jeni Tennison所著的Beginning XSLT 2.0: From Novice to Professional(2005)。此外,还有一些精彩的教程帮助你学习使用XPath:
l W3Schools的XPath教程:http://w3schools.com/xpath/。
l ZVON XPath 教程:http://zvon.org/xxl/XPathTutorial/General/examples.html。
目前,浏览器对XPath的支持各不相同:IE和Mozilla都比较完整(虽然不一样)地支持XPath实现,而Safari和Opera的版本还在开发中。作为补救,有几种完全使用JavaScript写的XPath实现方法。虽然它们通常会比较慢(跟基于浏览器的实现相比),但可以确保所有现代浏览器都能稳定运行:
l
XML for Script:http://xmljs.sf.net/。
l Google AJAXSLT:http://goog-ajaxslt.sf.net/。
此外,一个名为Sarissa的项目(http://sarissa.sf.net/)打算为各种浏览建立一个通用的实现包装。它可以让你一次写成XML访问代码,同时仍能从支持的浏览器中获得更快的运行速度。这项技术的最大问题在于,Opera和Safari浏览器对XPath支持仍不足,走不出XPath先前实现的困境。
使用浏览器内置的XPath与使用得到广泛支持的纯粹的JavaScript解决方案比起来,通常被认为是试验性质的技术。尽管如此,XPath的使用和普及渐渐成长起来,可把它当作CSS选择器强有力的竞争对手。
现在你已经掌握了定位任意DOM元素或任意DOM元素集合的必要知识和工具,那么我们现在应该利用这些知识了:从特性的操作到增加和删除DOM元素。






