使用Document对象

Document对象是通往DOM功能的入口,它向你提供了当前文档的信息,以及一组可供探索、导航、搜索或操作结构与内容的功能。

我们通过全局变量document访问Document对象,它是浏览器为我们创建的关键对象之一。Document对象向你提供文档的整体信息,并让你能够访问模型里的各个对象。要了解DOM,最好的方法是从一个例子开始。代码清单1展示的是前一章的示例文档,添加了一段脚本以演示某些基本的DOM功能。

代码清单1 使用Document对象

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用Document对象</title>
        <meta name="作者" content="黄子涵">
        <meta name="描述" content="使用Document对象">
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <p id="HZH_sky">
            听着自己的心跳<br>
            没有规则的跳跃<br>
            时间分割成对角<br>
            停止你对我的好<br>
            瓦解我们的依靠<br>
        </p>
        <p id="HZH_TaroLove">
            我们在白白净净的湖面<br>
            在海芋的季节<br>
            开满着美若天仙的海芋<br>
            缓缓慢慢的夏天<br>
            在热恋的季节<br>
            我坐在湖边开始轻描淡写<br>
        </p>
        <script>
            document.writeln("<pre>URL: " + document.URL);
            var elems = document.getElementsByTagName("p");
            for(var i = 0; i < elems.length; i++) {
                document.writeln("Element ID: " + elems[i].id);
                elems[i].style.border = "medium double black";
                elems[i].style.padding = "4px";
            }
            document.write("</pre>");
        </script>
    </body>
</html>

这段脚本简短,但它漂亮地集中了DOM的许多不同用途。下面我将把这段脚本分解成一个个片段并解释它们的作用。我们能对Document对象做的最基本操作之一是获取当前正在处理的HTML文档信息。这就是脚本的第一行所做的:

document.writeln("<pre>URL: " + document.URL);

在这个例子中,我读取了document.URL属性的值,它返回的是当前文档的URL。浏览器就是用这个URL载入此脚本所属文档的。

这条语句还调用了writeln方法:

document.writeln("<pre>URL: " + document.URL);

此方法会将内容附加到HTML文档的末尾。在这个例子中,我写入了pre元素的开始标签和URL属性的值。这就是一个非常简单的修改D0M范例,意思是我已经改变了文档的结构。

接下来,从文档中选择了一些元素:

var elems = document.getElementsByTagName("p");

有许多方法可以用于选择元素。getElementsByTagName选择属于某一给定类型的所有元素,在这个示例中是p元素。任何包含在文档里的p元素都会被该方法返回, 并被存放在一个名为elems的变量里。正如之前解释的,所有元素都是由HTMLElement对象代表的,它提供了基本的功能以代表HTML元素。getElementsByTagName方法返回的结果是HTMLElement对象所组成的一个集合。

有了可以处理的HTMLElement对象集合之后,我使用了一个for循环来列举集合里的内容,处理浏览器从HTML文档里找出的各个p元素:

for (var i = 0; i < elems.length; i++) {
     document.writeln("Element ID: " + elems[i].id); 
     elems[i].style.border = "medium double black"; 
     elems[i].style.padding = "4px";
}

对集合里的每个HTMLElement,我会读取它的id属性来获得id值,然后使用document.writeln方法把结果附加到之前生成的pre元素的内容上:

for (var i = 0; i < elems.length; i++) {
     document.writeln("Element ID: " + elems[i].id); 
     elems[i].style.border = "medium double black"; 
     elems[i].style.padding = "4px";
}

id属性是HTMLElement定义的众多属性之一。你可以使用这些属性来获取某个元素的信息,也可以对其进行修改(改动会同时应用到它所代表的HTML元素上)。在这个例子中,我使用了style属性来改变CSS border和padding属性的值:

for (var i = 0; i < elems.length; i++) {
    document.writeln("Element ID: " + elems[i].id); 
    elems[i].style.border = "medium double black"; 
    elems[i].style.padding = "4px";
} 

这些改动为每个元素都创建了一个内嵌样式,这些元素是你之前用getElementsByTagName方法找到的。当你修改某个对象时,浏览器会立即把改动应用到对应的元素上,在这个例子中是给这些p元素添加内边距和边框。

脚本的最后一行写入了pre元素的结束标签,就是我在脚本开头初始化的那个元素。我用的是write方法,它类似于writeln,但不会给添加到文档里的字符串附上行结束字符。这两种方法区别不大,除非你编写的内容是预格式化的,或者使用非标准的空白处理方式。

使用pre元素就意味着writein方法所添加的行结束字符会被用来构建内容。从下面中可以看到文档排列的效果。

【点击看效果】使用Document对象

使用Document元数据

Document对象的用途之一是向你提供关于文档的信息。下表介绍了你可以用来获取文档元数据的属性。

Document元数据属性

属 性 说 明 返 回
characterSet 返回文档的字符集编码。这是一个只读属性 字符串
charset 获取或设置文档的字符集编码 字符串
compatMode 获取文档的兼容性模式 字符串
cookie 获取或设置当前文档的cookie 字符串
defaultCharset 获取浏览器所使用的默认字符编码 字符串
defaultView 返回当前文档的Window对象 Window
dir 获取或设置文档的文本方向 字符串
domain 获取或设置当前文档的域名 字符串
implementation 提供可用DOM功能的信息 DOMImplementation
lastModified 返回文档的最后修改时间(如果修改时间不可用则返回当前时间) 字符串
location 提供当前文档的URL信息 Location
readyState 返回当前文档的状态。这是一个只读属性 字符串
referrer 返回链接到当前文档的文档URL(就是对应HTTP标头的值) 字符串
title 获取或设置当前文档的标题(即title元素的内容) 字符串

获取文档信息

你可以通过使用元数据属性来获得一些有用的文档信息,如代码清单2所示。

代码清单2 使用Document元素来获取元数据

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用Document元素来获取元数据</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用Document元素来获取元数据"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <script>
            document.writeln("<pre>");
            
            document.writeln("characterSet: " + document.characterSet);
            document.writeln("charset: " + document.charset);
            document.writeln("compatMode: " + document.compatMode);
            document.writeln("defaultCharset: " + document.defaultCharset);
            document.writeln("dir: " + document.dir);
            document.writeln("domain: " + document.domain);
            document.writeln("lastModified: " + document.lastModified);
            document.writeln("referrer: " + document.referrer);
            document.writeln("title: " + document.title);
            
            document.write("</pre>");
        </script>
    </body>
</html>

这些属性能帮助你加深对当前所处理文档的理解。你可以从下图看到这些在浏览器中显示的属性值。

【点击看效果】使用Document元素来获取元数据

理解怪异模式

compatMode属性告诉你浏览器是如何处理文档内容的。现如今存在着大量的非标准HTML,浏览器则试图显示出这类网页,哪怕它们并不遵循HTML规范。一些这样的内容依赖于浏览器的独特功能,而这些功能来源于浏览器依靠自身特点(而非遵循标准)进行竞争的年代。compatMode属性会返回两个值中的一个,如下表所示。

compatMode属性的值

说 明
CSS1Compat 此文档遵循某个有效的HTML规范(但不必是HTML5,有效的HTML4文档也会返回这个值)
BackCompat 此文档含有非标准的功能,已触发怪异模式

使用Location对象

document.location属性返回一个Location对象,这个对象给你提供了细粒度的文档地址信息,也允许你导航到其他文档上。下表介绍了Location对象里的函数和属性。

Location对象的方法和属性

属 性 说 明 返 回
protocol 获取或设置文档URL的协议部分 字符串
host 获取或设置文档URL的主机和端口部分 字符串
href 获取或设置当前文档的地址 字符串
hostname 获取或设置文档URL的主机名部分 字符串
port 获取或设置文档URL的端口部分 字符串
pathname 获取或设置文档URL的路径部分 字符串
search 获取或设置文档URL的查询(问号串)部分 字符串
hash 获取或设置文档URL的锚(井号串)部分 字符串
assign(<URL>) 导航到指定的URL上 void
replace(<URL>) 清除当前文档并导航到URL所指定的那个文档 void
reload() 重新载入当前的文档 void
resolveURL(<URL>) 将指定的相对URL解析成绝对URL 字符串

document.location属性最简单的用途是获取当前文档的地址信息,如代码清单3所示。

代码清单3 使用Location对象来获取文档信息

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用Location对象来获取文档信息</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用Location对象来获取文档信息"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <script>
            document.writeln("<pre>");
            
            document.writeln("protocol: " + document.location.hostname);
            document.writeln("host: " + document.location.host);
            document.writeln("hostname: " + document.location.hostname);
            document.writeln("port: " + document.location.port);
            document.writeln("pathname: " document.location.hash);
            document.writeln("search: " + document.location.search);
            document.writeln("hash: " + document.location.hash);
            
            document.write("</pre>");
        </script>
    </body>
</html>

search属性会返回URL的查询字符串部分,hash属性返回的则是URL片段。下图展示了各个Location属性就这个URL所返回的值。

没有效果,因为我的端口号是80。


提示

请注意当端口号为HTTP默认的80时,属性不会返回值。


使用Location对象导航到其他地方

你还可以使用Location对象(通过document.location属性)来导航到其他地方。具体的实现方式有好几种。首先,可以为之前示例用到的某个属性指派新值,如代码清单4所示。

代码清单4 通过给某个Location属性指派新值来导航到另一个文档上

<!DOCTYPE HTML>
<html>
    <head>
        <title>通过给某个Location属性指派新值来导航到另一个文档上</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="通过给某个Location属性指派新值来导航到另一个文档上"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<style>
		    #HZH_PennyTai {
			    width: 300px;
				height: 300px;
			}
		</style>
    </head>
    
    <body>
        <p>
            细腻的喜欢,毛毯般的厚重感<br>
			晒过太阳熟悉的安全感<br>
			分享热汤我们两支汤匙一个碗<br>
			左心房暖暖的好饱满<br>
        </p>
        
        <button id="HZH_pressme">黄子涵请你按下</button>
        <p>
            我明白 我要的爱<br>
			会把我宠坏<br>
            像一个小孩 只懂在你怀里坏<br>
            你要的爱 不只是依赖<br>
			要像个大男孩<br>
			风吹又日晒<br>
			生活自由自在<br>
			<br>
			谁愿压抑心中怒愤冲动<br>
			咒骂这虚与伪与假<br>
			从没信要屈膝面对生命<br>
			纵没有别人帮<br>
			一生只靠我双手<br>
			<br>
			无所谓慢慢来<br>
            迷宫一样的未来<br>
			无所谓就算爱<br>
            像空沙发在等待<br>
        </p>
        <img id="HZH_PennyTai" src="http://120.77.46.246/src/img/PennyTai.jpeg" alt="戴佩妮"/>
        <script>
            document.getElementById("HZH_pressme").onclick = function() {
                document.location.hash = "HZH_PennyTai";
            }
        </script>
    </body>
</html>    

这个例子包含了一个button元素,当它被点击时会给document.location.hash属性指派一个新值。我通过一个事件把按钮和点击时执行的JavaScript函数关联起来。

这一改动会让浏览器导航到某个id属性值匹配hash值的元素上,在这个案例里是img元素。从下面可以看到导航的效果。

【点击看效果】通过给某个Location属性指派新值来导航到另一个文档上

虽然我只是导航到了相同文档的不同位置,但也可以用Location对象的属性来导航到其他文档。不过,这种做法通常是用href属性实现的,因为你可以设置完整的URL。也可以使用Location对象定义的一些方法

assign和replace方法的区别在于,replace会把当前文档从浏览器历史中移除,这就意味着如果用户点击了后退按钮,浏览器就会跳过当前文档,就像它从未访问过该文档一样。代码清单5展示了如何使assign方法。

代码清单5 使用Location对象的assign方法进行导航

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用Location对象的assign方法进行导航</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用Location对象的assign方法进行导航"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <button id="HZH_pressme">黄子涵请你按下</button>
        <script>
            document.getElementById("HZH_pressme").onclick = function() {
                document.location.assign("https://www.sohu.com/a/335570535_761891");
            }
        </script>
    </body>
</html>

当用户点击button元素时,浏览器会导航到指定的URL上,在这个示例中是 https://www.sohu.com/a/335570535_761891

【点击看效果】使用Location对象的assign方法进行导航

读取和写入cookie

cookie属性让你可以读取、添加和更新文档所关联的cookie。代码清单6对此进行了演示。

代码清单6 读取和创建cookie

<!DOCTYPE HTML>
<html>
    <head>
        <title>读取和创建cookie</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="读取和创建cookie"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <p id="HZH_cookiedata">
            
        </p>
        <button id="HZH_write">黄子涵增加缓存</button>
        <button id="HZH_update">黄子涵更新缓存</button>
        <script>
            var cookieCount = 0;
            document.getElementById("HZH_update").onclick = updateCookie;
            document.getElementById("HZH_write").onclick = createCookie;
            readCookies();
            
            function readCookies() {
                document.getElementById("HZH_cookiedata").innerHTML = document.cookie;
            }
            
            function createCookie() {
                cookieCount++;
                document.cookie = "Cookie_" + cookieCount + "=value_" + cookieCount;
                readCookies();
            }
            
            function updateCookie() {
                document.cookie = "Cookie_" + cookieCount + "=Updated_" + cookieCount;
                readCookies();
            }
        </script>
    </body>
</html>    

cookie属性的工作方式稍微有点古怪。当你读取该属性的值时,会得到与文档相关联的所有cookie。 cookie是形式为name=value的名称/值对。如果存在多个cookie,那么cookie属性会把它们一起作为结果返回,之间以分号相隔,如name1=value1;name2=value2。

与之相对,当你想要创建新的cookie时,要指派一个新的名称/值对作为cookie属性的值,它将会被添加到文档的cookie集合。一次只能设置一个cookie。如果设置的值和现有的某个cookie具备相同的名称部分,那么就会用值部分更新那个cookie。

为了演示这一点,代码清单包含了一段脚本来读取、创建建和更新cookie。readCookies函数读取document.cookie属性的值,并将结果设置为某个段落(p)元素的内容。

这个文档里有两个button元素。当Add Cookie按钮被点击时,createCookie函数会给cookie属性指派一个新值,这个值会被添加到cookie集合中。Update Cookie按钮会调用updateCookie函数。这个函数给某个现有的cookie提供一个新值。从下面可以看到这段脚本的效果,不过为了对所发生的一切有亲身感受,建议你加载这个文档实际操作一下。

【点击看效果】读取和创建cookie

在这个示例中,我添加了三个cookie,其中一个已经被更新为某个新值。虽然添加cookie的默认形式是name=value,但你可以额外应用一些数据来改变cookie的处理方式。下表介绍了这些额外数据。

可以添加到cookie的额外字段

额外项 说 明
path=<path> 设置cookie关联的路径,如果没有指定则默认使用当前文档的路径
domain=<domain> 设置cookie关联的域名,如果没有指定则默认使用当前文档的域名
max-age=<seconds> 设置cookie的有效期,以秒的形式从它创建之时起开始计算
expires=<date> 设置cookie的有效期,用的是GMT格式的日期
secure 只有在安全(HTTPS)连接时才会发送cookie

这些额外的项目可以被附加到名称/值对的后面,以分号分隔,就像这样:

document.cookie = "MyCookie=MyValue;max-age=10";

理解就绪状态

document.readyState属性向你提供了加载和解析HTML文档过程中当前处于哪个阶段的信息。请记住,在默认情况下浏览器会在遇到文档里的script元素时立即开始执行脚本,但你可以使用defer属性推迟脚本的执行。正如我们在一些例子中所见到的,可以使用JavaScript的事件系统来独立执行各个函数,作为对文档变化或用户操作的反馈。

在所有这些情况下,了解浏览器加载和处理HTML到了哪个阶段可能会很有用。readyState 属性会返回三个不同的值,如表26-6所示。

readyState属性返回的值

说 明
loading 浏览器正在加载和处理此文档
interactive 文档已被解析,但浏览器还在加载其中链接的资源(图像和媒体文件等)
complete 文档已被解析,所有的资源也已加载完毕

随着浏览器逐步加载和处理文档,readyState属性的值从loading转为interactive,再转complete。这个属性和readystatechange事件结合使用时用处最大,该事件会在每次readyState属性的值发生变化时触发。代码清单7向你展示了如何同时使用这两个事件和属性来完成一项常见任务。

代码清单7 使用文档就绪状态来推迟脚本的执行

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用文档就绪状态来推迟脚本的执行</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用文档就绪状态来推迟脚本的执行"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
        <script>
            document.onreadystatechange = function() {
                if (document.readyState == "interactive") {
                    document.getElementById("HZH_pressme").onclick = function() {
                        document.getElementById("results").innerHTML = "Forget And Forgive";
                    }
                }
            }
        </script>
    </head>
    <body>
        <button id="HZH_pressme">Press Me</button>
        <pre id="results"></pre>
    </body>
</html>    

【点击看效果】使用文档就绪状态来推迟脚本的执行

这段脚本使用文档就绪状态来推迟一个函数的执行,直到文档进入interactive阶段。脚本代码要求能够找到在脚本执行时尚未被浏览器载入的文档元素。通过推迟脚本执行直至文档加载完成,我就能够确定这些元素是可以找到的。这种方式可以作为把script元素放到文档末尾的替代。

获取DOM的实现情况

document.implementation属性向你提供了浏览器对DOM功能的实现信息。这个属性返回一个 DOMImplementation对象,它包含一个你会感兴趣的方法:hasFeature方法。可以使用这个方法来判断哪些DOM功能已实现,如代码清单8所示。

代码清单8 使用document.implementation.hasFeature方法

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用document.implementation.hasFeature方法</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用document.implementation.hasFeature方法"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
    </head>
    
    <body>
        <script>
            
            var features = ["Core", "HTML", "CSS", "Selectors-API"];
            var levels = ["1.0", "2.0", "3.0"];
            
            document.writeln("<pre>");
            for(var i = 0; i < features.length; i++) {
                document.writeln("Checking for feature: " + features[i]);
                document.writeln(document.implementation.hasFeature(features[i]), levels[j]);
            }
            document.write("</pre>")
        </script>
    </body>
</html>    

【点击看效果】使用document.implementation.hasFeature方法

这段脚本检测了若干不同的DOM功能,以及所定义的功能等级。它并不像看上去那么有用。首先,浏览器并不总是能正确报告它们实现的功能。某些功能实现并不会通过hasFeature方法进行报告,而另一些报告了却根本没有实现。其次,浏览器报告了某项功能并不意味着它的实现方式是有用的。虽然这个问题不如以前严重,但DOM的实现是存在一些差别的。

如果你打算编写能在所有主流浏览器上工作的代码(你也应该这么想),那么hasFeature方法的用处不大。你应该选择在测试阶段全面检查代码,在需要的时候测试支持情况和备用措施,同时也可以考虑使用某个JavaScript库(例如jQuery),它可以帮助消除不同DOM实现之间的差别。

获取HTML元素对象

Document对象的一大关键功能是作为一个入口,让你能访问代表文档里各个元素的对象。可以用几种不同的方法来执行这个任务。有些属性会返回代表特定文档元素类型的对象,有些方法能让你很方便地运用条件搜索来找到匹配的元素,还可以将DOM视为一棵树并沿着它的结构进行导航。


提示

显而易见,获取这些对象的目的是对它们做一些有趣的事。


使用属性获取元素对象

Document对象为你提供了一组属性,它们会返回代表文档中特定元素或元素类型的对象。下表概述了这些属性。

Document对象的元素属性

属 性 说 明 返 回
activeElement 返回一个代表当前带有键盘焦点元素的对象 HTMLElement
body 返回一个代表body元素的对象 HTMLElement
Embeds、plugins 返回所有代表embed元素的对象 HTMLCollection
forms 返回所有代表form元素的对象 HTMLCollection
head 返回一个代表head元素的对象 HTMLHeadElement
images 返回所有代表img元素的对象 HTMLCollection
links 返回所有代表文档里具备href属性的a和area元素的对象 HTMLCollection
scripts 返回所有代表script元素的对象 HTMLCollection

下表里描述的大多数属性都返回一个HTMLCollection对象。DOM就是用这种方式来表示一组代表元素的对象集合。代码清单9演示了访问集合内对象的两种方法。

代码清单9 使用HTMLCollection对象

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用HTMLCollection对象</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用HTMLCollection对象"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
			
			img {
			    width: 300px;
				height: 300px;
			}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <img id="HZH_jay_chou" src="http://120.77.46.246/src/img/jay_chou.jpeg" alt="周杰伦"/>
        <p>
            玻璃上有雾气在被隐藏起过去<br>
			这巷弄太过弯曲走不回故事里<br>
			电影院的座椅,隔遥远的距离<br> 
        </p>
        <img id="HZH_LeonLaiMing" src="http://120.77.46.246/src/img/LeonLaiMing.jpeg" alt="黎明"/>
        <p>
            你我曾在梦里<br> 
			暗中相约在这夏<br>
            承诺站在夕照后<br> 
			斜阳别你渐离去<br>
        </p>
        <img id="HZH_EasonChan" src="http://120.77.46.246/src/img/EasonChan.jpeg" alt="陈奕迅"/>
        <script>
            var resultElement = document.getElementById("HZH_results");
            
            var elems = document.images;
            
            for(var i = 0; i < elems.length; i++) {
                resultElement.innerHTML += "图像元素: " + elems[i].id + "\n";
            }
            var srcValue = elems.namedItem("HZH_LeonLaiMing").src;
            resultElement.innerHTML += "HZH_LeonLaiMing元素的src是: " + srcValue + "\n";
        </script>
    </body>
</html>    

【点击看效果】使用HTMLCollection对象

第一种使用HTMLCollection对象的方法是将它视为一个数组。它的length属性会返回集合里的项目数量,它还支持使用标准的JavaScript数组索引标记(element[i]这种表示法)来直接访问集合里的各个对象。这就是我在示例里用的第一种方法,在此之前我已经用document.images属性获得了一个HTMLCollection,它包含了所有代表文档里img元素的对象。


提示

请注意我使用了innerHTML属性来设置pre元素的内容。


第二种方法是使用namedItem方法,它会返回集合里带有指定id或name属性值(如果有的话)的项目。这就是我在示例中用的第二种方法,我使用了namedItem方法来获取代表某个特定img所素的对象,该元素的
id属性值为apple。


提示

请注意我读取了其中一个对象的src属性值。这个属性由HTMLImageElement对象实现,后者被用于代表img元素。我使用的另一个属性(id)是HTMLElement的一部分,因此对所有类型的元素都可用。

使用数组标记获取已命名元素

还可以使用数组风格的标记来获取代表某个已命名元素(named element)的对象。它指的是带有id或name属性值的元素。代码清单10提供了一个例子。

代码清单10 获取已命名元素的对象

<!DOCTYPE HTML>
<html>
    <head>
        <title>获取已命名元素的对象</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="获取已命名元素的对象"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
			img {
			    width: 300px;
				height: 300px;
			}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <img id="HZH_KousukeAtari" name="image" src="http://120.77.46.246/src/img/KousukeAtari.jpeg" alt="中孝介"/>
        <p>
            你的梦想盛开<br>
            迎著风紧紧握住船舵<br>
            我只能祈祷<br>
            你能朝著梦想中桃花源前进<br>
        </p>
        <img id="HZH_SallyYeh" name="image" src="http://120.77.46.246/src/img/SallyYeh.jpeg" alt="叶倩文"/>
        <p>
            零时十分倚窗看门外暗灯<br>
            迷途夜雨静吻路人<br>
			绵绵夜雨 无言泪珠<br>
        </p>
        <img id="HZH_Pakho" src="http://120.77.46.246/src/img/Pakho.jpeg" alt="周柏豪"/>
        <script>
            var resultsElement = document.getElementById("HZH_results");
            var elems = document["HZH_SallyYeh"];
            
            if (elems.namedItem) {
                for(var i = 0; i < elems.length; i++) {
                    resultsElement.innerHTML += "图像元素:" + elems[i].id + "\n";
                }
            } else {
                resultsElement.innerHTML += "元素的Src为:" + elems.src + "\n";
            }
        </script>
    </body>
</html>    

【点击看效果】获取已命名元素的对象

可以看到,我使用了数组风格的索引标记来获取代表某个id值为apple元素的对象。用这种方法获取元素的特别之处在于,根据文档内容或元素排列顺序的不同,可能会得到不同种类的结果。

浏览器以深度优先(depth-first)的顺序看待文档里的所有元素,尝试将id或name属性与指定的值进行匹配。如果第一次匹配到的是一个id属性,那么浏览器就会停止搜索(因为id值在文档里必须是唯一的)并返回一个代表匹配元素的HTMLElement对象。

如果第一次匹配到的是一个name属性值,那么你将得到的或者是一个HTMLElement(如果只有一个匹配的元素),或者是一个HTMLCollection(如果有不止一个匹配的元素)。浏览器开始匹配name值后就不会再匹配id值了。

可以看到,我把namedItem属性当做一项测试来判断得到的是哪一种结果。在这个例子里得到的是一个HTMLElement,因为我指定的值匹配了一个id值。


提示

也可以将已命名元素视为属性。举个例子,document[apple]和document.apple的意思是一样的。


搜索元素

Document对象定义了许多方法,可以用它们搜索文档里的元素。下表介绍了这些方法。

寻找元素的Document方法

属 性 说 明 返 回
getElementByld(<id>) 返回带有指定id值的元素 HTMLElement
getElementsByClassName(<class>) 返回带有指定class值的元素 HTMLElement[]
getElementsByName(<name>) 返回带有指定name值的元素 HTMLElement[]
getElementsByTagName(<tag>) 返回指定类型的元素 HTMLElement[]
querySelector(<selector>) 返回匹配指定CSS选择器的第一个元素 HTMLElement
querySelectorAll(<selector>) 返回匹配指定CSS选择器的所有元素 HTMLElement[]

正如你可能预计的那样,这些方法中的一些会返回多个元素。我在表里将它们展现为返回一个HTMLElement对象数组,但严格来说并非如此。事实上,这些方法返回一个NodeList,它是底层DOM规范的一部分,处理的是通用结构文档格式,而不仅仅是HTML。但是,对这些用途而言,你可以将它们视为数组,把注意力集中在HTML5上。

这些搜索方法可以被分成两类。代码清单11演示了其中的第一类,即名称由getElement开头的那些方法。

代码清单11 使用document.getElement开头的方法

<!DOCTYPE HTML>
<html>
    <head>
        <title>使用document.getElement开头的方法</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用document.getElement开头的方法"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
			img {
			    width: 300px;
				height: 300px;
			}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <img id="HZH_JackyCheung" class="歌手" name="张学友" src="http://120.77.46.246/src/img/JackyCheung.jpeg" alt="张学友"/>
        <p>
            回头当天的一切像泡影<br>
            原来天荒地老总会明<br>
			遥遥长路寻背影<br>
            暖暖爱去如流星<br>
        </p>
        <img id="HZH_FayeWong" src="http://120.77.46.246/src/img/FayeWong.jpeg" alt="王菲"/>
		<p>
            就算天空再深看不出裂痕<br>
            眉头仍聚满密云<br>
            历史在重演这么烦嚣城中<br>
            没理由相恋可以没有暗涌<br>
        </p>       
		<img id="HZH_EasonChan" src="http://120.77.46.246/src/img/EasonChan.jpeg" alt="陈奕迅"/>
        <script>
            var resultsElement = document.getElementById("HZH_results");
            
            var pElems = document.getElementsByTagName("p");
            resultsElement.innerHTML += "这里有" + pElems.length + "个p元素。\n";
            
            var songerElems = document.getElementsByClassName("歌手");
            resultsElement.innerHTML += "歌手类中有" +songerElems.length
                + "个元素。\n";
            
            var nameElems = document.getElementsByName("张学友");
            resultsElement.innerHTML += "这里有" + nameElems.length
                + "个名字为张学友的元素。";
        </script>
    </body>
</html>    

【点击看效果】使用document.getElement开头的方法

这些方法的功能跟你预料得差不多,而且你只需记住一种行为。在使用getElementByld方法 时,如果找不到带有指定id值的元素,浏览器就会返回null。与之相对,其他的方法总是会返回一个HTMLElement对象数组,但如果找不到匹配,length属性就会返回0。

用CSS选择器进行搜索

使用css选择器是一种有用的替代性搜索方式。选择器让你可以在文档里找到范围更广的元 素。我在第17章和第18章介绍了CSS选择器。代码清单26-12演示了用这种方式获取元素对象。

代码清单12 使用CSS选择器获取元素对象
<!DOCTYPE HTML>
<html>
    <head>
        <title>使用CSS选择器获取元素对象</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="使用CSS选择器获取元素对象"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
			#HZH_Beyond, #HZH_SHE {
			    width: 300px;
				height: 300px;
			}
			#HZH_Bolbbalgan4 {
			    width: 400px;
				height: 300px;
			}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <img id="HZH_Beyond" class="歌手" name="Beyond" src="http://120.77.46.246/src/img/Beyond.jpeg" alt="Beyond"/>
        <p>
            忘掉远方是否可有出路<br>
            忘掉夜里月黑风高<br>
            踏雪过山双脚虽渐老<br>
            但靠两手一切达到<br>
        </p>
        <img id="HZH_SHE" class="歌手图像" name="SHE" src="http://120.77.46.246/src/img/SHE.jpeg" alt="SHE"/>
        <p>
            拉长耳朵提高警觉<br>
            神经细胞全面戒备<br>
			心跳的声音 蹦蹦重低音<br>
        </p>
        <img id="HZH_Bolbbalgan4" src="http://120.77.46.246/src/img/Bolbbalgan4.jpeg" alt="Bolbbalgan4"/>
        <script>
            var resultsElement = document.getElementById("HZH_results");
            
            var elems = document.querySelectorAll("p, img#HZH_SHE")
            resultsElement.innerHTML += "选择器匹配" + elems.length
                + "个元素。\n";
        </script>
    </body>
</html>    

【点击看效果】使用CSS选择器获取元素对象

我在这个例子里使用了一个选择器,它会匹配所有的p元素和id值为apple的img元素。用其他document方法很难达到同样的效果,而且我发现自己使用选择器的比例要高于使用getElement方法。

合并进行链式搜索

DOM的一个实用功能是几乎所有Document对象实现的搜索方法同时也能被HTMLElement对象实现(一个例外),这让你可以合并进行链式搜索。唯一的例外是getElementByld方法,只有Document对象才能使用它。代码清单13演示了链式搜索。

代码清单13 合并进行链式搜索

<!DOCTYPE HTML>
<html>
    <head>
        <title>合并进行链式搜索</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="合并进行链式搜索"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <p id="HZH_JJLin">
            <span id="HZH_heartbeat">心跳</span>乱了节奏<br>
			梦也不自由<br>
			爱是个绝对<span id="HZH_promise">承诺</span>不说<br>
			撑到一千年以后<br>
			放任无奈淹没<span="HZH_dust">尘埃</span><br>
			我在废墟之中守着你走来喔<br>
			我的<span="HZH_Tears">泪光</span>承载不了喔<br>
			所有一切你要的爱<br>
        </p>
        <script>
            var resultsElement = document.getElementById("HZH_results");
            
			var elems = document.getElementById("HZH_JJLin").getElementsByTagName("span");
			resultsElement.innerHTML += "这里有" + elems.length + "个span元素。\n";
			
            var elems2 = document.getElementById("HZH_JJLin").querySelectorAll("span");
            resultsElement.innerHTML += "这里有" + elems2.length
                + "个span元素(Mix)。\n";
				
            var selElems = document.querySelectorAll("#HZH_JJLin > span");
            resultsElement.innerHTML += "这里有" + selElems.length
                + "个span元素(CSS)。\n";
        </script>
    </body>
</html>    

【点击看效果】合并进行链式搜索

这个例子里有两次链式搜索,这两次都从getElementById方法开始(它会返回之后进行处理的单个对象)。在第一个例子中,我用getElementsByTagName方法链接了一个搜索,在第二个例子中则通过querySelectorAll方法使用了一个非常简单的CSS选择器。这些例子都返回了一个span元素的集合,它们都位于id为tblock的p元素之内。

当然,也可以通过单独给Document对象应用CSS选择器方法来实现同样的效果,但是这一功能在某些情况下会很方便,比如处理由脚本中的其他函数(或第三方脚本)所生成的HTMLElement对象。从下图可以看到这些搜索的结果。

在DOM树里导航

另一种搜索元素的方法是将DOM视为一棵树,然后在它的层级结构里导航。所有的DOM对象都支持一组属性和方法来让我们做到这一点,下表对它们进行了介绍。

navigation属性和方法

属 性 说 明 返 回
childNodes 返回子元素组 HTMLElement[]
firstChild 返回第一个子元素 HTMLElement
hasChildNodes() 如果当前元素有子元素就返回true 布尔值
lastchild 返回倒数第一个子元素 HIMLElement
nextSibling 返回定义在当前元素之后的兄弟元素 HTMLElement
parentNode 返回父元素 HTMLElement
previousSibling 返回定义在当前元素之前的兄弟元素 HTMLElement

代码清单14展示了一段脚本,它能让你导航到文档各处,并在一个pre元素里显示当前所选元素的信息。

代码清单14 在DOM树里导航

<!DOCTYPE HTML>
<html>
    <head>
        <title>在DOM树里导航</title>
        <meta name="作者" content="黄子涵"/>
        <meta name="描述" content="在DOM树里导航"/>
        <link rel="shortcut icon" href="http://120.77.46.246/src/img/ba_favicon.ico" type="image/x-icon"/>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>
            pre {border: medium double black;}
			#HZH_Twins {
			    width: 193px;
				height: 270px;
			}
			#HZH_AmeiChang {
			    width: 320px;
				height: 256px;
			}
        </style>
    </head>
    
    <body>
        <pre id="HZH_results"></pre>
        <p id="HZH_FIR">
            回忆里想起模糊的小时候<br>
			云朵漂浮在蓝蓝的天空<br>
			那时的你说 要和我手牵手<br>
			一起走到时间的尽头<br>
        </p>
        <img id="HZH_Twins" class="歌手图像" name="Twins" src="http://120.77.46.246/src/img/Twins.jpeg" alt="Twins"/>
        <img id="HZH_AmeiChang" src="http://120.77.46.246/src/img/AmeiChang.jpeg" alt="张惠妹" />
        <p>
            
        </p>
        <p>
            <button id="HZH_parent">父母</button>
            <button id="HZH_child">第一个孩子</button>
            <button id="HZH_prev">前兄弟姐妹</button>
            <button id="HZH_next">下一个兄弟姐妹</button>
        </p>
        
        <script>
            var resultsElem = document.getElementById("HZH_results");
            var element = document.body;
            
            var buttons = document.getElementsByTagName("button");
            for(var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = handleButtonClick;
            }
            
            processNewElement(element);
            
            function handleButtonClick(e) {
                if (element.style) {
                    element.style.backgroundColor = "white";
                }
                
                if (e.target.id == "HZH_parent" && element != document.body) {
                    element = element.parentNode;
                } else if (e.target.id == "HZH_child" && element.hasChildNodes()) {
                    element = element.previousSibling;
                } else if (e.target.id == "HZH_next" && element.nextSibling) {
                    element = element.nextSibling;
                }
                
                processNewElement(element);
                if (element.style) {
                    element.style.backgroundColor = "lightgrey";
                }
            }
            
            function processNewElement(elem) {
                resultsElem.innerHTML = "元素类型:" + elem + "\n";
                resultsElem.innerHTML += "元素ID:" + elem.id + "\n";
                resultsElem.innerHTML += "是否有孩子结点:"
                    + elem.hasChildNodes() + "\n";
                if (elem.previousSibling) {
                    resultsElem.innerHTML += ("前兄弟姐妹是:" + elem.previousSibling + "\n");
                } else {
                    resultsElem.innerHTML += "没有前兄弟姐妹\n";
                }
                if (elem.nextSibling) {
                    resultsElem.innerHTML += "下一个兄弟姐妹是:" + elem.nextSibling + "\n";
                } else {
                    resultsElem.innerHTML += "没有下一个兄弟姐妹:\n";
                }
            }
        </script>
    </body>
</html>    

这段脚本的重要之处用粗体进行显示,它们是实际进行导航操作的部分。脚本的其余部分则是在做准备工作,处理按钮点击以及显示当前所选元素的信息。从下面可以看到这段脚本的效果。

【点击看效果】在DOM树里导航