第 25 章 理解DOM
在这一部分,你将开始探索文档对象模型(DOM)。通过使用DOM,你能够添加、移除和操作各种元素。还可以使用事件(event)来响应用户的交互操作,以及完全控制CSS。
从这里开始,你就处于HTML5的程序设计部分了。在此之前,你已经用元素和CSS声明创建了内容,现在是时候以程序员的身份开始使用JavaScript了。
理解文档对象模型
DOM是一组对象的集合,这些对象代表了HTML文档里的各个元素。顾名思义,DOM就像一个模型,它由代表文档的众多对象组成。DOM是Web开发的关键工具之一,它是HTML文档的结构和内容与JavaScript之间的桥梁。作为示例,代码清单1展示了一个简单的HTML文档。
代码清单1 一个简单的HTML文档
<!DOCTYPE>
<html>
<head>
<title>一个简单的HTML文档</title>
<meta name="作者" content="黄子涵"/>
<meta name="描述" content="一个简单的例子"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p id="FloatingLife">
<span id="monologue">独白:</span><br>
<br>
当你意识到自己其实已经不再那么年轻<br>
每天周而复始的过着自己可能并不喜欢却无法改变的生活<br>
做着自己并不喜欢的工作<br>
人生的选择题<br>
是要继续甘于平庸<br>
还是奋力一搏去追求自己所想要的<br>
成功或者失败<br>
得到或者失去<br>
也往往不那么重要<br>
因为我们只想要当下<br>
</p>
<P id="DifficultSutra">
吞风吻雨葬落日未曾彷徨<br>
欺山赶海践雪径也未绝望<br>
拈花把酒偏折煞世人情狂<br>
凭这两眼与百臂或千手不能防<br>
天阔阔雪漫漫共谁同航<br>
这沙滚滚水皱皱笑着浪荡<br>
贪欢一晌偏教那女儿情长埋葬<br>
</P>
</body>
</html>
从上图可以看到浏览器是如何显示上述示例HTML文档的。
作为显示HTML文档过程中的一个步骤,浏览器会解析HTML并创建一个模型。这个模型保存了各个HTML元素之间的层级关系(如下图所示),每个元素都由一个JavaScript对象表示。
你可以用DOM来获取文档信息,也可以对其进行修改。这是现代Web应用程序的基础。
模型里的每一个对象都有若干属性和方法。当你用它们来修改对象的状态时,浏览器会让这些改动反映到对应的HTML元素上,并更新你的文档。
所有代表元素的DOM象都支持同一组基本功能:HTMLElement对象和其定义的核心功能始终是可用的,无论对象代表何种元素都是如此。另外,某些对象会定义一些额外的功能,这些操作反映了特定HTML元素独一无二的特性。记住下面的一点很重要:文档模型里任何代表某个元素的对象都至少能支持HTMLElement功能,其中一些还支持额外的功能。
不是所有可供使用的对象都代表了HTML元素。正如你即将看到的,一些对象代表元素的集合,另一些则代表DOM自身的信息,当然还有Document这个对象,它是我们探索DOM的入口。
注意
如果你熟悉面向对象程序设计的概念,那么了解HTMLElement是一个接口可能会有所帮助,它是由DOM所包含的对象实现的。用于代表具体元素的对象是HTMLElement派生出来的接口,意思是你既可以把某个对象当做HTMLElement的实现,也可以当做其更为具体的子类型。如果你不熟悉面向对象的概念也不用担心。对主流Web程序设计而言,是否理解它们无关紧要。简单起见,下面将把所有的一切都称为对象。
理解DOM Level和兼容性
开始使用DOM时,你会碰到网络上一些文章和教程提到DOM Level(DOM等级,比如某个特定功能是在DOM Level3中定义的)。DOM Level是标准化过程中的版本号,在大多数情况下应该忽略它们。
DOM的标准化过程并不是完全成功的,每一个DOM Level都有描述它的标准和文档,但它们并没有被完整地实现,浏览器只是简单地挑选了其中的有用功能,而忽略了其他的。更糟糕的是,已经实现的功能之间还存在着某种程度的不一致性。
部分问题在于,DOM规范与HTML标准过去是分别开发的。HTML5试图通过包含一组必须实现的DOM核心功能来解决这个问题。然而这种做法尚未见效,碎片化仍然存在。
有多种方式可用来应对DOM功能的多变性。第一种方式是使用某个JavaScript库(比如jQuery),它消除了浏览器之间实现方式的差别。使用库的优点在于其一致性,但缺点是只能使用库支持的那些功能。如果想突破库原有功能的局限,就只能转回到直接操作DOM上,并重新面对之前的那些问题。(这并不是说jQuery和类似的库没有价值,它们很有用,非常值得去了解一下。)
第二种是保守方式:只使用你所知的被广泛支持的那些功能。这种方式一般来说是最为明智的,不过它需要仔细而全面的测试。不仅如此,你还必须仔细测试新版的浏览器,确保对这些功能的支持没有发生变化或者被移除。
测试DOM功能
第三种方式是测试与某一功能相关的DOM对象属性或方法是否存在。代码清单2包含了一个简单的例子。
代码清单2 测试某个功能
<!DOCTYPE>
<html>
<head>
<title>测试某个功能</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p id="MoveBack">
天空灰得像哭过<br>
酸酸的空气<br>
嗅出我们的距离<br>
一幕锥心的结局<br>
像呼吸般无法停息<br>
抽屉泛黄的日记<br>
榨干了回忆<br>
<img src="http://120.77.46.246/src/img/Xu_Weizhou.jpeg">
</p>
<script>
var images;
if (document.querySelectorAll) {
images = document.querySelectorAll("#MoveBack > img");
} else {
images = document.getElementById("MoveBack").getElementsByTagName("img");
}
for (var i = 0; i < images.length; i++) {
images[i].style.border = "thick soild balck";
images[i].style.padding = "4px";
}
</script>
</body>
</html>
在这个例子里,脚本使用一条if语句来判断document对象是否定义了一个名为querySelectorAll的方法。如果这条语句的计算结果是true,那么浏览器是支持这一功能的,我就可以开始使用它。如果该语句的计算结果是false,那么我可以换一种方式来达到同样的目的。
谈到DOM时,你经常看到的就是刚才这种建议,但它过于草率,没有指出其中的缺陷,而这些缺陷有可能很严重。
第一个缺陷在于,并不总是存在另一种方式能实现某个给定功能的效果。代码清单2能顺利工作是因为我测试的功能是其他函数的一种便利性加强形式,但情况并不总是如此。
第二个缺陷是我只测试了该功能是否存在,而不是它的实现质量和一致性。许多功能(特别是新的功能)需要多个版本的浏览器才能稳定下来并实现一致性。虽然这个问题不像以前那样严重,但你也许很容易就会遇到意料之外的结果,因为你依赖的浏览器功能实现方式存在差别。
第三个缺陷是必须测试每一种你所依赖的功能。这么做需要耗费大量的精力,而且产生的代码充斥着无穷无尽的测试。我并不是说它不是一种有用的技巧,而是它存在缺陷,不应该取代恰当的测试。
DOM快速查询
以下部分提供了对象、方法、属性和事件的快速查询。
Document的成员
Document对象,它代表当前的文档,也是你探索DOM的入口。下表概述了此对象所定义的成员。
Document对象
名 称 | 说 明 | 返 回 |
---|---|---|
activeElement | 返回代表文档中当前获得焦点元素的对象 | HTMLElement |
body | 返回代表文档中body元素的对象 | HTMLElement |
characterSet | 返回文档的字符集编码。这是一个只读属性 | 字符串 |
charset | 获取或设置文档的字符集编码 | 字符串 |
childNodes | 返回子元素的集合 | HTMLElement[] |
compatMode | 获取文档的兼容性模式 | 字符串 |
cookie | 获取或设置当前文档的cookie | 字符串 |
defaultCharset | 获取浏览器所使用的默认字符编码 | 字符串 |
defaultview | 返回当前文档的Window对象 | Window |
dir | 获取或设置文档的文本方向 | 字符串 |
domain | 获取或设置当前文档的域名 | 字符串 |
embeds、plugins | 返回所有代表文档中embed元素的对象 | HTMLCollection |
firstChild | 返回某个元素的第一个子元素 | HTMLElement |
forms | 返回所有代表文档中form元素的对象 | HTMLCollection |
getElementById(<id >) |
返回带有指定id值的元素 | HTMLElement |
getElementsByClassName(<class >) |
返回带有指定class值的元素 | HTMLElement[] |
getElementsByName(<name >) |
返回带有指定name值的元素 | HTMLElement[] |
getElementsByTagName(<tag >) |
返回带有指定类型的元素 | HTMLElement[] |
hasChildNodes() | 如果当前元素有子元素则返回true | 布尔值 |
head | 返回代表head元素的对象 | HTMLHeadElement |
images | 返回所有代表img元素的对象 | HTMLCollection |
implementation | 提供关于DOM可用功能的信息 | DOMImplementation |
lastchild | 返回最后一个子元素 | HTMLElement |
lastModified | 返回文档的最后修改时间 | 字符串 |
links | 返回所有代表文档中具备href的a和area元素的对象 | HTMLCollection |
location | 提供关于当前文档URL的信息 | Location |
nextSibling | 返回位于当前元素之后的兄弟元素 | HTMLElement |
parentNode | 返回父元素 | HTMLElement |
previousSibling | 返回位于当前元素之前的兄弟元素 | HTMLElement |
querySelector(<selector >) |
返回匹配特定CSS选择器的第一个元素 | HTMLElement |
querySelectorAll(<selector >) |
返回匹配特定CSS选择器的所有元素 | HTMLElement[] |
readyState | 返回当前文档的状态 | 字符串 |
referrer | 返回链接到当前文档的文档URL (它是对应HTTP标头的值) | 字符串 |
scripts | 返回所有代表script元素的对象 | HTMLCollection |
title | 获取或设置当前文档的标题 | 字符串 |
下表概述了Location对象。
Location 对象
名 称 | 说 明 | 返 回 |
---|---|---|
assign(<URL >) |
导航到指定的URL上 | void |
hash | 获取或设置文档URL的锚(井号串)部分 | 字符串 |
host | 获取或设置文档URL的主机名和端口号部分 | 字符串 |
hostname | 获取或设置文档URL的主机名部分 | 字符串 |
href | 获取或设置当前文档的地址 | 字符串 |
pathname | 获取或设置文档URL的路径部分 | 字符串 |
port | 获取或设置文档URL的端口号部分 | 字符串 |
protocol | 获取或设置文档URL的协议部分 | 字符串 |
reload() | 重新加载当前文档 | void |
replace(<URL >) |
清除当前文档并导航至URL所指定的新文档 | void |
resolveURL(<URL >) |
将指定的相对URL解析为绝对URL | 字符串 |
search | 获取或设置文档URL的查询(问号串)部分 | 字符串 |
Window 的成员
Window对象
名 称 | 说 明 | 返 回 |
---|---|---|
alert(<msg >) |
向用户显示一个对话框窗口并等候其被关闭 | void |
blur() | 让窗口失去键盘焦点 | void |
clearlnterval(<id >) |
撤销某个时间间隔计时器 | void |
clearTimeout(<id >) |
撤销某个超时计时器 | void |
close() | 关闭窗口 | void |
confirm(<msg >) |
显示一个带有确认和取消提示的对话框窗口 | 布尔值 |
defaultView | 返回活动文档的Window | Window |
document | 返回与此窗口关联的Document对象 | Document |
focus() | 让窗口获得键盘焦点 | void |
frames | 返回文档内嵌iframe元素的Window对象数组 | Window[] |
history | 提供对浏览器历史的访问 | History |
innerHeight | 获取窗口内容区域的高度 | 数值 |
innerWidth | 获取窗口内容区域的宽度 | 数值 |
length | 返回文档内嵌的iframe元素数量 | 数值 |
location | 提供当前文档地址的详细信息 | Location |
opener | 返回打开当前浏览器上下文环境Window | Window |
outerHeight | 获取窗口的高度,包括边框和菜单栏等 | 数值 |
outerWidth | 获取窗口的宽度,包括边框和菜单栏等 | 数值 |
pageXOffet | 获取窗口从左上角算起水平滚动过的像素数 | 数值 |
pageYOffset | 获取窗口从左上角算起垂直滚动过的像素数 | 数值 |
parent | 返回当前Window的父Window | Window |
postMessage(<msg >,<origin >) |
给另一个文档发送消息 | void |
print() | 提示用户打印页面 | void |
prompt(<msg >, <val >) |
显示对话框提示用户输入一个值 | 字符串 |
screen | 返回一个描述屏幕的Screed象 | Screen |
screenLeft、ScreenX | 获取从窗口左边缘到屏幕左边缘的像素数 | 数值 |
screenTop、screenY | 获取从窗口上边缘到屏幕上边缘的像素数 | 数值 |
scrollBy(<x >, <y >) |
让文档相对其当前位置进行滚动 | void |
scrollTo(<x >, <y >) |
滚动到指定的位置 | void |
self | 返回当前文档的Window | Window |
setInterval(<function >, <time >) |
创建一个计时器,每隔time毫秒调用指定的函数 | 整数 |
setTimeout(<function >, <time >) |
创建一个计时器,等待time毫秒后调用指定的函数 | 整数 |
showModalDialog(<url >) |
弹出一个窗口,显示指定的URL | void |
stop() | 停止载入文档 | void |
top | 返回最上层的Window | Window |
下表概述了History对象的成员。
History对象
名称 | 说 明 | 返 回 |
---|---|---|
back() | 在浏览历史里后退一步 | void |
forward() | 在浏览历史里前进一步 | void |
go(<index >) |
转到相对于当前文档的某个浏览历史位置。正值是前进,负值是后退 | void |
length | 返回浏览历史里的项目数量 | 数值 |
pushState(<state >, <title >, <url >) |
向浏览器历史添加一个条目 | void |
replaceState(<state >, <title >, <url >) |
替换浏览器历史中的当前条目 | void |
state | 返回浏览器历史里关联当前文档的状态数据 | 对象 |
下表概述了Screen对象的成员。
Screen对象
名 称 | 说 明 | 返 回 |
---|---|---|
availHeight | 返回屏幕上可供显示窗口部分的高度(排除工具栏之类) | 数值 |
availWidth | 返回屏幕上可供显示窗口部分的宽度(排除工具栏之类) | 数值 |
colorDepth | 返回屏幕的颜色深度 | 数值 |
height | 返回屏幕的高度 | 数值 |
width | 返回屏幕的宽度 | 数值 |
HTMLElement的成员
HTMLElement对象,它代表了文档里的各种HTML元素。下表概述了此对象定义的成员。
HTMLElement对象
名 称 | 说 明 | 返 回 |
---|---|---|
checked | 获取或设置checked属性的存在状态 | 布尔值 |
classList | 获取或设置元素所属类的列表 | DOMTokenList |
className | 获取或设置元素所属类的列表 | 字符串 |
dir | 获取或设置dir属性的值 | 字符串 |
disabled | 获取或设置disabled属性的存在状态 | 布尔值 |
hidden | 获取或设置hidden属性的存在状态 | 布尔值 |
id | 获取或设置id属性的值 | 字符串 |
lang | 获取或设置lang属性的值 | 字符串 |
spellcheck | 获取或设置spellcheck属性的存在状态 | 布尔值 |
tabIndex | 获取或设置tabindex属性的值 | 数值 |
tagName | 返回标签名(象征元素的类型) | 字符串 |
title | 获取或设置title属性的值 | 字符串 |
add(<class >) |
给元素添加指定的类 | void |
contains(<class >) |
如果元素属于指定的类则返回true | 布尔值 |
length | 返回元素所属类的数量 | 数值 |
remove(<class >) |
从元素上移除指定的类 | void |
toggle(<class >) |
如果类不存在就添加它,如果存在则移除它 | 布尔值 |
attributes | 返回应用到元素上的属性 | Attr[] |
dataset | 返回以data-开头的属性 | 字符串数组[<name >] |
getAttribute(<name >) |
返回指定属性的值 | 字符串 |
hasAttribute(<name >) |
如果元素带有指定属性则返回true | 布尔值 |
removeAttribute(<name >) |
从元素上移除指定属性 | void |
setAttribute(<name >, <value >) |
应用一个指定名称和值的属性 | void |
appendChild(HTMLElement) | 将指定元素附加为当前元素的子元素 | HTMLElement |
cloneNode(boolean) | 复制某个元素 | HTMLElement |
compareDocumentPosition(HTMLElement) | 判断某个元素的相对位置 | 数值 |
innerHTML | 获取或设置元素的内容 | 字符串 |
insertAdjacentHTML(〈pos 〉,<text >) |
相对于元素的位置插入HTML | void |
insertBefore(<newelem >, <childElem >) |
将第一个元素插入到第二个(子)元素之前 | HTMLElement |
isEqualNode(<HTMLElement >) |
判断指定元素是否与当前元素等同 | 布尔值 |
isSameNode(HTMLElement) | 判断指定元素是否就是当前元素 | 布尔值 |
outerHTML | 获取或设置某个元素的HTML和内容 | 字符串 |
removeChild(HTMLElement) | 从当前元素上移除指定的子元素 | HTMLElement |
replaceChild(HTMLElement, HTMLElement) | 替换当前元素的某个子元素 | HTMLElement |
createElement(<tag >) |
用指定标签类型创建一个新的HTMLElement对象 | HTMLElement |
createTextNode(<text >) |
用指定内容创建一个新的Text对象 | Text |
Text对象,它用于代表文档中的文本内容。下表描述了Text对象的成员。
Text对象
名 称 | 说 明 | 返 回 |
---|---|---|
appendData(<string >) |
在文本块的末尾附加指定字符串 | void |
data | 获取或设置文本 | 字符串 |
deleteData(<offset >, <count >) |
移除字符串中的文本。第一个数字是偏移量,第二个数字是要移除的字符数量 | void |
insertData(<offset >, <string >) |
在指定的偏移量位置插入指定字符串 | void |
length | 返回字符数量 | 数值 |
replaceData(<offset >, <count >, <string >) |
用指定字符串替换一部分文本 | void |
replaceWholeText(<string >) |
替换全部文本 | Text |
splitText(<number >) |
将现有的Text元素在指定的偏移量处一分为二。 | Text |
substringData(<offset >, <count >) |
返回文本的子串 | 字符串 |
wholeText | 获取文本 | 字符串 |
DOM里的CSS属性
下表列出了CSSStyleDeclaration对象的属性和它们所对应的样式。
CSSStyleDeclaration对象的成员
成 员 | 对应于 |
---|---|
background | background |
backgroundAttachment | background-attachment |
backgroundColor | background-color |
backgroundimage | background-image |
backgroundposition | background-position |
backgroundRepeat | background-repeat |
border | border |
borderBottom | border-bottom |
borderBottomColor | border-bottom-color |
borderBottomStyle | border-bottom-style |
borderBottomWidth | border-bottom-width |
borderCollapse | border-collapse |
borderColor | border-color |
borderLeft | border-left |
borderLeftColor | border-left-color |
borderLeftStyle | border-left-style |
borderLeftWidth | border-left-width |
borderRight | border-right |
borderRightColor | border-right-color |
borderRightStyle | border-right-style |
borderRightWidth | border-iight-width |
borderSpacing | border-spacing |
borderStyle | border-style |
borderTop | border-top |
borderTopColor | border-top-color |
borderTopStyle | boider-top-style |
borderTopWidth | border-top-width |
borderWidth | border-width |
captionSide | caption-side |
clear | clear |
color | color |
cssFloat | float |
cursor | cursor |
direction | direction |
display | display |
emptyCells | empty-cells |
font | font |
fontFamily | font-family |
fontSize | font-size |
FontStyle | font-style |
fontVariant | font-variant |
fontweight | font-weight |
height | height |
letterSpacing | letter-spacing |
lineHeight | line-height |
liststyle | list-style |
listStylelmage | list-style-image |
listStylePosition | list-style-position |
listStyleType | list-style-type |
margin | margin |
marginBottom | margin-bottom |
marginLeft | margin-left |
marginRight | margin-right |
marginTop | margin-top |
maxHeight | max-height |
maxWidth | max-width |
minHeight | min-height |
minWidth | min-width |
outline | outline |
outlineColor | outline-color |
outlineStyle | outline-style |
outlineWidth | outline-width |
overflow | overflow |
padding | padding |
paddingBottom | padding-bottom |
paddingLeft | padding-left |
paddingRight | padding-right |
paddingTop | padding-top |
tableLayout | table-layout |
textAlign | text-align |
textDecoration | text-decoration |
textindent | text-indent |
textshadow | text-shadow |
textTransform | text-transform |
visibility | visibility |
whitespace | whitespace |
width | width |
wordSpacing | word-spacing |
zIndex | z-index |
DOM中的事件
DOM的事件系统,有许多不同的事件可供使用,如下表所示。
DOM的事件
名 称 | 说 明 |
---|---|
blur | 在元素失去键盘焦点时触发 |
click | 在按下鼠标按钮后释放时触发 |
dblclick | 在两次按下鼠标按钮并释放时触发 |
focus | 在元素获得键盘焦点时触发 |
focusin | 在元素即将获得键盘焦点时触发 |
focusout | 在元素即将失去键盘焦点时触发 |
keydown | 在用户按下某个键时触发 |
keypress | 在用户按下某个键并释放时触发 |
keyup | 在用户释放某个键时触发 |
mousedown | 在鼠标按钮被按下时触发 |
mouseenter | 在光标移入元素或其下属元素所占据的屏幕区域时触发 |
mouseleave | 在光标移出元素及其所有下属元素所占据的屏幕区域时触发 |
mousemove | 在光标位于元素上方并移动时触发 |
mouseout | 与mouseleave相似,区别是当光标还在下属元素上方时此事件也会被触发 |
mouseover | 与mouseenter相似,区别是当光标还在下属元素上方时此事件也会被触发 |
mouseup | 在鼠标按钮被释放时触发 |
onabort | 在文档或资源的加载过程被中止时触发 |
onafterprint | 在用户打印文档后触发 |
onbeforeprint | 在调用Window.print()方法之后,向用户呈现打印选项之前触发 |
onerror | 在文档或资源载入岀错时触发 |
onhashchange | 在地址的锚(井号串)部分变动时触发 |
onload | 在文档或资源载入完成时触发 |
onpopstate | 触发时会提供一个关联浏览器历史的状态对象 |
onresize | 在窗口大小改变时触发 |
onunload | 在文档从窗口或浏览器中卸载时触发 |
readystatechange | 在ready State属性的值改变时触发 |
reset | 在某张表单被重置时触发 |
submit | 在某张表单被提交时触发 |