Document对象是通往DOM功能的入口,它向你提供了当前文档的信息,以及一组可供探索、导航、搜索或操作结构与内容的功能。
我们通过全局变量document访问Document对象,它是浏览器为我们创建的关键对象之一。Document对象向你提供文档的整体信息,并让你能够访问模型里的各个对象。要了解DOM,最好的方法是从一个例子开始。代码清单1展示的是前一章的示例文档,添加了一段脚本以演示某些基本的DOM功能。
<!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对象的用途之一是向你提供关于文档的信息。下表介绍了你可以用来获取文档元数据的属性。
属 性 | 说 明 | 返 回 |
---|---|---|
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所示。
<!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属性会返回两个值中的一个,如下表所示。
值 | 说 明 |
---|---|
CSS1Compat | 此文档遵循某个有效的HTML规范(但不必是HTML5,有效的HTML4文档也会返回这个值) |
BackCompat | 此文档含有非标准的功能,已触发怪异模式 |
document.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所示。
<!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对象(通过document.location属性)来导航到其他地方。具体的实现方式有好几种。首先,可以为之前示例用到的某个属性指派新值,如代码清单4所示。
<!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方法。
<!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。代码清单6对此进行了演示。
<!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的处理方式。下表介绍了这些额外数据。
额外项 | 说 明 |
---|---|
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所示。
值 | 说 明 |
---|---|
loading | 浏览器正在加载和处理此文档 |
interactive | 文档已被解析,但浏览器还在加载其中链接的资源(图像和媒体文件等) |
complete | 文档已被解析,所有的资源也已加载完毕 |
随着浏览器逐步加载和处理文档,readyState属性的值从loading转为interactive,再转complete。这个属性和readystatechange事件结合使用时用处最大,该事件会在每次readyState属性的值发生变化时触发。代码清单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元素放到文档末尾的替代。
document.implementation属性向你提供了浏览器对DOM功能的实现信息。这个属性返回一个 DOMImplementation对象,它包含一个你会感兴趣的方法:hasFeature方法。可以使用这个方法来判断哪些DOM功能已实现,如代码清单8所示。
<!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实现之间的差别。
Document对象的一大关键功能是作为一个入口,让你能访问代表文档里各个元素的对象。可以用几种不同的方法来执行这个任务。有些属性会返回代表特定文档元素类型的对象,有些方法能让你很方便地运用条件搜索来找到匹配的元素,还可以将DOM视为一棵树并沿着它的结构进行导航。
显而易见,获取这些对象的目的是对它们做一些有趣的事。
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演示了访问集合内对象的两种方法。
<!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提供了一个例子。
<!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对象定义了许多方法,可以用它们搜索文档里的元素。下表介绍了这些方法。
属 性 | 说 明 | 返 回 |
---|---|---|
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开头的那些方法。
<!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选择器是一种有用的替代性搜索方式。选择器让你可以在文档里找到范围更广的元 素。我在第17章和第18章介绍了CSS选择器。代码清单26-12演示了用这种方式获取元素对象。
<!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演示了链式搜索。
<!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对象都支持一组属性和方法来让我们做到这一点,下表对它们进行了介绍。
属 性 | 说 明 | 返 回 |
---|---|---|
childNodes | 返回子元素组 | HTMLElement[] |
firstChild | 返回第一个子元素 | HTMLElement |
hasChildNodes() | 如果当前元素有子元素就返回true | 布尔值 |
lastchild | 返回倒数第一个子元素 | HIMLElement |
nextSibling | 返回定义在当前元素之后的兄弟元素 | HTMLElement |
parentNode | 返回父元素 | HTMLElement |
previousSibling | 返回定义在当前元素之前的兄弟元素 | HTMLElement |
代码清单14展示了一段脚本,它能让你导航到文档各处,并在一个pre元素里显示当前所选元素的信息。
<!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树里导航