原文:The Definitive Guide to HTML5
协议:CC BY-NC-SA 4.0
二十三、过渡、动画和变换
在这一章中,我将介绍三种不同的方法来将简单的特效应用到 HTML 元素中:过渡,动画,以及变换。我将在本章的后面解释和演示这些术语。这三个特性都是 CSS3 中的新特性,在我撰写本文时,它们只通过特定于浏览器的前缀得到支持。这是我期望很快改变的事情,因为这些特性将会非常受网页设计者和开发者的欢迎。
将效果应用于 HTML 元素并不是一个新的想法,大多数优秀的 JavaScript 库都至少包含一些现在已经融入到 CSS3 中的效果。与 JavaScript 相比,使用 CSS3 的优势在于性能。许多新功能是关于随着时间的推移改变 CSS 属性的值,这是可以直接在浏览器引擎中以较少的开销处理的事情。尽管如此,这些效果(即使是基本的)也会占用大量的处理能力,尤其是在复杂的网页上。因此,你应该谨慎使用我在本章中描述的效果。让用户的计算机陷入停顿总是不受欢迎的,尤其是如果你只是在炫耀你的动画技巧。
不经常使用这些效果的另一个原因是它们会极大地分散注意力和令人讨厌。使用这些效果来增强用户在页面上执行的任务——不管是什么任务——不要将效果应用到与该任务无关的元素上。表 23-1 提供了本章的总结。
使用过渡
浏览器通常会立即将 CSS 属性中的更改应用到元素中。例如,如果您使用:hover
选择器,用户一将鼠标移到元素上,浏览器就会应用您与选择器相关联的属性。清单 23-1 给出了一个例子。
清单 23-1。立即应用新的属性值
`
There are lots of different kinds of fruit - there are over 500 varieties of **banana** alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
`在这个例子中,有一个span
元素,它有两种特定的样式。一种样式是通用的(使用选择器#banana
),另一种样式只在用户将鼠标移动到元素上时应用(使用选择器#banana:hover
)。
提示我在这个例子中使用了color
属性。你可以在第二十四章中了解更多关于这个属性的信息。
当用户将鼠标移动到span
元素上时,浏览器会做出响应,并立即应用新的属性值。你可以在图 23-1 中看到变化。
图 23-1。改变 CSS 属性值的直接应用
CSS 转换功能允许您控制新属性值的应用速度。因此,例如,您可以选择逐渐改变示例中的span
元素的外观,使鼠标移动到单词banana
上的效果不那么不协调。表 23-2 描述了允许你这样做的属性。
transition-delay
和transition-duration
属性被指定为 CSS 时间,这是一个后跟ms
(表示毫秒)或s
(表示秒)的数字。
transition
速记属性的格式如下:
transition: <transition-property> <transition-duration> <transition-timing-function> <transition-delay>
清单 23-2 展示了如何将一个过渡应用到示例 HTML 文档中。当我写这篇文章时,没有一个主流浏览器直接支持转换属性,但是,除了 Internet Explorer,所有浏览器都使用特定于浏览器的前缀来实现这些属性。我在清单中使用了前缀–webkit
。
注意使用标准属性的主流浏览器还没有实现动画特性。与转换非常相似,除了 Internet Explorer 之外的所有浏览器都使用特定于浏览器的前缀来实现该功能。在清单 23-2 中,我使用了–webkit
前缀,这意味着这个例子将适用于 Safari 和 Chrome。如果你想使用 Firefox 或 Opera,只需用–webkit
代替–moz
或–o
。这是第三次综合培训中另一个重要的改进领域,我期望它将很快得到适当的实现。
清单 23-2。使用过渡
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
` ` `在这个例子中,我添加了一个通过#banana:hover
选择器应用到样式的过渡。该转换将在用户将鼠标移动到span
元素上 100 毫秒后开始,持续 500 毫秒,并应用于background-color
、color
、padding
、font-size
和border
属性。图 23-2 显示了过渡的渐进过程。
图 23-2。过渡的逐步应用
请注意我是如何在示例中指定了多个属性的。每个过渡属性都采用逗号分隔的值,这样您就可以获得并发过渡效果。您也可以为延迟和持续时间指定多个值,这意味着不同的属性转换在不同的时间开始,并运行不同的持续时间。
创建反向转换
仅当应用了与转场相关联的样式时,转场才会生效。我的示例样式使用了:hover
选择器,这意味着只有当用户的鼠标在span
元素上时才应用样式。一旦用户将鼠标从span
元素移开,只有#banana
样式会被应用,默认情况下,元素的外观会立即恢复到原始状态。
正是因为这个原因,大多数转换都是成对出现的:转换到临时状态,然后反向转换。清单 23-3 展示了如何通过应用第二个过渡平滑地返回到原始风格。
清单 23-3。创建第二个过渡
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
`在这个例子中,我省略了transition-property
属性。这将导致所有属性更改在整个过渡期间逐渐应用。我还指定了 10 毫秒的初始延迟和 250 毫秒的持续时间。添加一个简短的反向转换会使返回到原始状态的声音不那么刺耳。
提示浏览器在第一次布局页面时不应用过渡。这意味着当 HTML 文档第一次显示时,立即应用#banana
样式中的属性,然后通过转换逐渐应用。
选择如何计算中间值
当您使用转换时,浏览器必须计算出每个属性的初始值和最终值之间的中间值。您可以使用transition-timing-function
属性来指定确定中间值的方式,用一组代表三次贝塞尔曲线的四个点来表示。有五种预设曲线可供选择,分别由以下值表示:
ease
(默认值)linear
ease-in
ease-out
ease-in-out
你可以在图 23-3 中看到每条曲线。该线显示了中间值随时间向最终值发展的速率。
图 23-3。时序函数曲线
理解这些值的最简单方法是在自己的 HTML 文档中进行实验。还有一个额外的值cubic-bezier
,允许您指定自定义曲线。然而,我的经验是,过渡并不像它们应该的那样平滑,缺乏粒度会破坏这些值中的大部分,并且使得指定定制曲线变得毫无意义。希望实现会随着它们在最终标准上的融合而改进。清单 23-4 展示了transition-timing-function
属性的应用。
清单 23-4。使用过渡计时功能属性
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
`我已经选择了linear
值,这是我发现给我的过渡最少的值。
使用动画
CSS 动画本质上是增强的过渡。在如何从一种 CSS 样式转换到另一种样式时,您有更多的选择、更多的控制和更多的灵活性。表 23-3 描述了动画特性。
animation
速记属性的格式如下:
animation: <animation-name> <animation-duration> <animation-timing-function> <animation-delay> <animation-iteration-count>
请注意,这些属性都不允许您指定将被动画显示的 CSS 属性。这是因为动画分为两部分定义。第一部分包含在样式声明中,并使用表 23-3 中所示的属性。这定义了动画的风格,但是没有定义什么是动画。第二部分是用@key-frames
规则创建的,用于定义动画将应用的属性集。你可以在清单 23-5 中看到动画的两个部分。
清单 23-5。制作动画
`
#banana:hover {
-webkit-animation-delay: 100ms;
-webkit-animation-duration: 500ms;
** -webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-name: ‘GrowShrink’;
}
@-webkit-keyframes GrowShrink {
to {
font-size: x-large;
border: medium solid white;
background-color: green;
color: white;
padding: 4px;
}
}**
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other
well-known fruit, we are faced with thousands of choices.
`
要理解本例中发生的情况,您必须查看动画的两个部分。第一部分是使用样式中的动画属性和#banana:hover
选择器。让我们从基本属性开始:动画将在样式应用后 100 毫秒开始,持续时间为 500 毫秒,将无限重复,中间值将使用linear
函数计算。除了重复动画之外,这些属性在转场中有直接的对应项。
这些基本属性没有描述将被动画化的属性集。为此,我需要使用animation-name
属性。通过将该属性的值设置为GrowShrink
,我已经指示浏览器找到一组被称为GrowShrink
的关键帧,并使用基本属性的值将关键帧指定的属性制作成动画。下面是清单中的关键帧声明(我已经去掉了–webkit
前缀):
**@keyframes GrowShrink { to { font-size: x-large; border: medium solid white; background-color: green; color: white; padding: 4px; } }**
我用@keyframes
开始声明,然后指定这个集合的名称。在这种情况下,名称为GrowShrink
。在声明中,我指定了将被激活的属性集。在本例中,我在一个to
声明中指定了五个属性及其值。这是最简单的一种关键帧集合。to
声明定义了动画的属性集和动画结束时这些属性的最终值。(稍后我将向您展示更复杂的关键帧。)在应用样式之前,动画的初始值取自动画元素的属性值。
清单中的动画类似于我在本章前面用于过渡的例子,当您在浏览器中查看 HTML 文档并将鼠标移动到span
元素上时,效果看起来也是一样的。至少一开始看起来是一样的,然后动画又重复了一遍,这是第一个区别。span
元素的大小增加,达到最大值,然后返回到原始状态,此时动画重新开始。你可以在图 23-4 中看到效果。
图 23-4。动画中的重复状态
使用关键帧
CSS 动画的关键帧方面非常灵活,非常值得探索。在接下来的章节中,我将展示一些不同的方法来表达关键帧,以创造更复杂的效果。
设置初始状态
在前面的例子中,动画属性的初始值取自元素本身。您可以使用from
子句指定一组可选的初始值,如清单 23-6 所示。
清单 23-6。指定另一个初始状态
`…
…`
在这个例子中,我已经为font-size
和background-color
属性提供了初始值。在to
条款中指定的其他属性的初始值将在动画开始时从元素中获取。你可以在图 23-5 中看到新条款的效果。在动画开始时,span
元素的文本大小和背景颜色切换到在from
子句中指定的初始值。
图 23-5。用 from 子句设置初始状态
指定中间关键帧
您可以添加额外的关键帧来定义动画中的中间阶段。你可以通过添加百分比子句来实现,如清单 23-7 所示。
清单 23-7。添加中间关键帧
`…
…`
对于每个百分比子句,您可以在动画中定义该子句中指定的属性和值应该完全应用的点。在这个例子中,我定义了一个50%
和一个75%
子句。
中间关键帧有两种用途。第一个是为属性定义一个新的变化率。我这样做是为了padding
房产。在中间点(由50%
子句定义),动画元素的填充将是1px
。在75%
时,它将是2px
,到动画结束时它将被设置为4px
。浏览器将使用由animation-timing-function
属性指定的计时函数计算从一个关键帧移动到另一个关键帧所需的值的级数,给出从一个关键帧到下一个关键帧的平滑级数。
提示如果你愿意,在定义第一个和最后一个关键帧时,你可以用0%
和100%
代替from
和to
。
中间关键帧的另一个用途是定义值以创建更复杂的动画。我已经用background-color
属性做到了这一点。初始值(red
)在from
条款中定义。在百分之五十点,该值将是yellow
,在动画结束时,它将是green
。通过添加一个非连续的中间值,我在一个动画中创建了两个颜色过渡:red
到yellow
和yellow
到green
注意,我没有在75%
子句中提供中间值。这是因为您不必为每个关键帧提供值。你可以在图 23-6 中看到新关键帧的效果。
图 23-6。添加中间关键帧
设置重复方向
当您将动画设置为重复播放时,您可以选择当浏览器到达动画结尾时会发生什么。您可以使用animation-direction
属性指定您的偏好,使用表 23-4 中描述的值。
您可以在清单 23-8 的中看到animation-direction
属性。
清单 23-8。使用动画方向属性
`
#banana:hover {
-webkit-animation-delay: 100ms;
-webkit-animation-duration: 250ms;
-webkit-animation-iteration-count: 2;
-webkit-animation-timing-function: linear;
-webkit-animation-name: ‘GrowShrink’;
-webkit-animation-direction: alternate;
}
@-webkit-keyframes GrowShrink {
to {
font-size: x-large;
border: medium solid white;
background-color: green;
padding: 4px;
}
}
`
在这个例子中,我使用了animation-iteration-count
属性来指定动画应该只执行两次。在第二次迭代结束时,动画元素将返回到其原始状态。我已经为animation-direction
属性使用了alternate
值,这样动画可以向前和向后播放。你可以在图 23-7 中看到效果。
图 23-7。将动画的方向设置为交替
如果我为animation-iteration-count
属性使用了infinite
值,那么只要鼠标停留在span
元素上,动画就会前后播放,产生一个简单的脉冲效果。
normal
值使动画跳回起点,每次迭代向前播放。你可以在图 23-8 中看到它的效果。
图 23-8。将动画的方向设置为正常
了解最终状态
CSS 动画的限制之一是,由动画中的关键帧定义的属性值仅在动画本身期间应用。在动画结束时,动画元素的外观将恢复到其原始状态。清单 23-9 给出了一个例子。
清单 23-9。动画结束时动画状态丢失
`…
…`
你可以在图 23-8 中看到这产生的效果。即使鼠标仍然停留在span
元素上,一旦动画完成,元素的外观就会被重置。
图 23-9。动画完成后元素的还原外观
发生这种情况的原因是因为 CSS 动画制作了一个新样式的应用,但本身并没有进行任何持久的更改。如果要在动画结束时保留元素的外观,必须使用本章前面所述的过渡。
将动画应用到初始布局
动画优于过渡的一个优点是,您可以将它们应用于页面的初始布局。清单 23-10 给出了一个例子。
清单 23-10。在初始布局制作元素动画
`…
…`
在这个例子中,我用#banana
选择器定义了样式中的动画。加载页面时会自动应用该样式,这意味着浏览器一显示 HTML 就应用动画。
提示你应该特别小心地使用这种方法。当你不响应用户操作时,应该尽量少用动画,动画效果应该是微妙的,不要妨碍用户阅读或与更宽的页面交互。
重用关键帧
您可以将同一组关键帧用于多个动画,每个动画都可以配置不同的动画属性值。清单 23-11 给出了一个演示。
清单 23-11。跨多个动画重用关键帧
`
span {
font-size: large;
border: medium solid black;
}
**#banana {
-webkit-animation-duration: 2500ms;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-timing-function: linear;
-webkit-animation-name: ‘ColorSwap’;
}
#apple {
-webkit-animation-duration: 500ms;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: normal;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-name: ‘ColorSwap’;
}**
@-webkit-keyframes ColorSwap {
to {
border: medium solid white;
background-color: green;
}
}
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the` ` countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
`清单 23-11 显示了两种风格,每一种都使用了ColorSwap
关键帧。与#apple
选择器相关的动画将使用不同的计时功能在一个短方向上播放,也将向前播放。
将多个动画应用于多个元素
前一个例子的一个变化是以同一个动画的多个元素为目标。您可以通过扩展包含动画细节的样式的选择器的范围来做到这一点,如清单 23-12 所示。
清单 23-12。瞄准多个元素
`…
…`
在这个例子中,文档中的两个span
元素都被选择器匹配,所以两者都将使用相同的关键帧和相同的配置来制作动画。你可以在图 23-10 中看到效果。
图 23-10。用同一动画制作多个元素的动画
您也可以将多个动画应用于一个元素,只需将逗号分隔的值添加到动画属性中。清单 23-13 展示了如何将多个关键帧应用到一个元素中。
清单 23-13。对单个元素应用多个关键帧
`…
…`
在这个例子中,我将ColorSwap
和GrowShrink
关键帧应用到了#banana
和#apple
元素上。浏览器将同时应用两个关键帧。
停止和开始动画
您可以通过animation-play-state
属性停止和恢复动画。当该属性的值为paused
时,动画将暂停。值playing
将恢复动画。清单 23-14 展示了如何使用 JavaScript 来改变这个属性的值。我将在本书的第四部分解释如何在类似的情况下使用 JavaScript。
清单 23-14。停止和启动动画
`
#banana {
-webkit-animation-duration: 2500ms;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-timing-function: linear;
-webkit-animation-name: ‘GrowShrink’;
}
@-webkit-keyframes GrowShrink {
from {
font-size: large;
border: medium solid black;
}
to {
font-size: x-large;
border: medium solid white;
background-color: green;
color: white;
padding: 4px;
}
}
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other
well-known fruit, we are faced with thousands of choices.
Running
Paused
`
使用变换
CSS 变换允许你对元素应用线性变换,这意味着你可以旋转、缩放、倾斜和平移元素。表 23-5 显示了用于应用变换的属性。
应用变换
您可以通过transform
属性对元素应用变换。该属性的允许值是一组预定义的函数,如表 23-6 所述。
你可以在清单 23-15 中看到一个转换的例子。和本章中的其他 CSS 特性一样,主流浏览器还没有直接实现转换。我在清单中使用了前缀–moz
,因为 Firefox 拥有最完整的实现。
清单 23-15。对元素应用变换
`
There are lots of different kinds of fruit - there are over 500 varieties of **banana** alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
`在这个例子中,我向#banana
样式添加了一个transform
属性声明,指定了两个转换。第一个是旋转-45deg
(即逆时针旋转 45 度),第二个是沿 x 轴缩放1.2
因子。你可以在图 23-11 中看到这些变换的效果。
图 23-11。旋转和缩放元素
正如您所看到的,元素已经按照指定的方式进行了旋转和缩放。请注意,页面的布局并没有为了适应转换而改变。元素会覆盖周围的一些内容。
指明起源
transform-origin
属性允许您指定应用变换的原点。默认情况下,使用元素的中心,但是您可以使用表 23-7 中描述的值选择不同的原点。
要定义一个值,需要为每个 x 轴和 y 轴提供一个值。如果只提供一个值,第二个值被假定为center
。清单 23-16 展示了transform-origin
属性的使用。
清单 23-16。使用变换原点属性
`
font-size: x-large;
border: medium solid white;
background-color: green;
color: white;
padding: 4px;
-moz-transform: rotate(-45deg) scaleX(1.2);
-moz-transform-origin: right top;
}
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other
well-known fruit, we are faced with thousands of choices.
`
在这个例子中,我把原点移到了元素的右上角。你可以在图 23-12 中看到它的效果。
图 23-12。指定变换的原点
动画制作和变换
您可以将动画和过渡应用于变换,就像处理任何其他 CSS 属性一样。清单 23-17 包含了一个演示。
清单 23-17。将过渡应用于变换
`
#banana:hover {
-moz-transition-duration: 1.5s;
-moz-transform: rotate(360deg);
}
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other
well-known fruit, we are faced with thousands of choices.
`
在本例中,我定义了一个过渡,它将在 1.5 秒的时间内应用 360 度旋转变换。当用户将鼠标悬停在span
元素上时,将应用这种转换。你可以在图 23-13 中看到效果。
图 23-13。将过渡与变换相结合
总结
在这一章中,我向你展示了 CSS3 中的三个新特性,它们给了你对元素外观的巨大控制权。过渡、变换和动画易于使用,并提供合理的性能和极大的灵活性。我建议谨慎使用这些功能,但是谨慎应用可以增强网页和应用的外观和整体用户体验。我在本章中使用了特定于浏览器的前缀,但是其实现非常接近标准,我希望浏览器很快会提供对真实属性名称的支持。
二十四、其他 CSS 属性和功能
在这一章中,我用不适合其他章节的属性来结束我对 CSS 的介绍。这些都是重要而有用的属性,但是我找不到一种方法将它们融入其他章节的主题中。在这一章中,你将看到如何设置元素的前景色和不透明度,以及如何对 HTML 表格和列表元素应用特殊的样式。表 24-1 提供了本章的总结。
设置元素颜色和透明度
在本书的这一部分中,您已经看到了 CSS 颜色的不同用法,如background-color
属性、border-color
属性等等。还有两个与颜色相关的属性。表 24-2 描述了这些属性。
设置前景色
属性为元素设置前景色。原则上,元素可以对颜色属性的含义有不同的解释,但实际上,color
属性设置了文本的颜色。清单 24-1 显示了正在使用的color
属性。
清单 24-1。使用颜色属性
`
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other well-known fruit, we are faced
with thousands of choices.
Learn more about Bananas
`
在这个例子中,我使用了两次color
属性:一次是设置span
元素的前景色和透明度,另一次是当鼠标悬停在a
元素上时设置它们的前景色。你可以在图 24-1 中看到效果。这种效果在打印的页面上可能很难辨认。为了理解这种效果,您应该在浏览器中显示示例 HTML 文档。
图 24-1。使用颜色属性设置前景
设置元素不透明度
注意,在前面的例子中,我使用了rgba
函数来设置span
元素的颜色。我通过提供一个小于 1 的 alpha 值使文本变得稍微透明。从图中可能很难看出,但效果是文本允许一些背景颜色显示出来。
您可以使用opacity
属性使整个元素及其文本内容透明。该属性的允许范围是从零(表示完全透明)到 1(表示完全不透明)。清单 24-2 显示了正在使用的opacity
属性。
清单 24-2。使用不透明度属性
`
There are lots of different kinds of fruit - there are over 500 varieties of **banana** alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices. Learn more about Bananas
`在这个例子中,我将span
元素的不透明度设置为0.4
。效果如图图 24-2 所示,但在打印页面上可能很难辨认。
图 24-2。设置元素的不透明度
造型表
有许多属性可以让你设计出table
元素的独特特征,我在第十一章中介绍过。表 24-3 总结了这些特性。
折叠表格边框
属性让你控制浏览器为元素绘制边框的方式。你可以在图 24-3 中看到默认的方法。
图 24-3。带边框的表格的默认外观
浏览器在表格周围画一个边框,在每个单元格周围画一个边框,创造出双边框效果。你可以通过应用border-collapse
属性来解决这个问题,如清单 24-3 所示。
清单 24-3。使用边框折叠属性
`
Rank | Name | Color | Size & Votes | |
---|---|---|---|---|
Favorite: | Apples | Green | Medium | 500 |
2nd Favorite: | Oranges | Orange | Large | 450 |
© 2011 Adam Freeman Fruit Data Enterprises |
collapse
值告诉浏览器你不希望在相邻元素的每一条边上都画边框。你可以在图 24-4 中看到它的效果。
图 24-4。折叠表格的边框
配置分隔边框
如果您使用默认的separate
值作为border-collapse
属性,您可以使用一些额外的属性来优化外观。属性border-spacing
定义了相邻元素的边界之间将要绘制的空间量,如清单 24-4 中的所示。
清单 24-4。使用边框间距属性
`
Rank | Name | Color | Size & Votes | |
---|---|---|---|---|
Favorite: | Apples | Green | Medium | 500 |
2nd Favorite: | Oranges | Orange | ||
© 2011 Adam Freeman Fruit Data Enterprises |
在这个例子中,我指定了 10 像素的边框间距。你可以在图 24-5 中看到效果。
图 24-5。使用边框间距属性
处理空单元格
您还可以告诉浏览器如何处理空单元格。默认情况下,当单元格为空时,浏览器会绘制一个单独的边框,如图 24-5 所示。您可以使用empty-cells
属性来控制这种行为。默认的show
值创建了图 24-3 中的效果,而hide
值告诉浏览器不要绘制边框。清单 24-5 显示了在前一个例子的style
元素中添加了empty-cells
属性。
清单 24-5。使用空单元格属性
<style> table { border-collapse: separate; border-spacing: 10px; **empty-cells: hide;** } th, td { padding: 2px; } </style>
你可以在图 24-6 中看到这种变化的效果。
图 24-6。使用空单元格属性
定位标题
正如我在第十一章中解释的,当你将一个caption
元素添加到一个table
中时,它会显示在表格的顶部,即使它不是第一个子元素。您可以使用caption-side
属性来改变这种行为。该属性有两个值:top
(默认)和bottom
。清单 24-6 显示了这个属性的应用。
清单 24-6。使用标题侧属性
`
Rank | Name | Color | Size & Votes | |
---|---|---|---|---|
Favorite: | Apples | Green | Medium | 500 |
2nd Favorite: | Oranges | Orange | ||
© 2011 Adam Freeman Fruit Data Enterprises |
你可以在图 24-7 中看到这个属性的效果。
图 24-7。使用标题侧属性移动标题
指定表格布局
默认情况下,浏览器根据每列中最宽的单元格来设置表格的宽度。这意味着您不必担心自己计算大小,但也意味着浏览器必须接收所有的表格内容,然后才能确定页面的布局。
浏览器显示表格的方式由table-layout
属性控制,默认值(如上所述)由值auto
设置。您可以使用另一个允许值fixed
禁用自动布局。在固定模式下,表格的大小由表格和各个列的width
值设置。如果没有可用的列宽信息,浏览器将在各列之间平均分配空间。
因此,浏览器能够在仅接收一行表格数据后确定每列的宽度。对后续行的数据进行包装以使其适合(这可能导致行比在auto
模式下的行高)。
清单 24-7 显示了正在使用的table-layout
属性。
清单 24-7。使用表格布局属性
`
Rank | Name | Color | Size & Votes | |
---|---|---|---|---|
Really Really Really Long Title: | Apples | Green | Medium | 500 |
2nd Favorite: | Oranges | Orange | ||
© 2011 Adam Freeman Fruit Data Enterprises |
在这个例子中,我将table
元素的width
设置为占据 100%的可用空间,并将布局样式设置为fixed
。我还更改了第二行中一个单元格的内容,以展示对布局的影响,如图 24-8 中的所示。
图 24-8。使用表格布局属性
请注意可用空间是如何在五列中平均分配的,以及第二行中的长标题是如何换行的,从而使该行比其他行高得多。
造型列表
有许多特定于样式列表的属性。表 24-4 总结了这些特性。
list-style
速记属性的格式如下:
list-style: <list-style-type> <list-style-position> <list-style-image>
设置列表标记类型
使用list-style-type
属性为列表设置标记(有时也称为项目符号)的样式。您可以在表 24-5 中看到该属性的允许值。
表 24-5 仅显示了部分可用样式。还有更多,代表不同的字母,符号风格和数字约定。您可以在[www.w3.org/TR/css3-lists](http://www.w3.org/TR/css3-lists)
找到完整的列表。清单 24-8 显示了使用中的list-style-type
属性。
清单 24-8。使用列表样式类型属性
`
I also like:
- bananas
- mangoes
- cherries
- plums
- peaches
- grapes
`
您可以将该属性应用于整个列表或单个列表项。在这个例子中,我两者都做了(尽管结果对读者来说没有意义)。你可以在图 24-9 中看到效果。
图 24-9。设置列表标记类型
使用图像作为列表标记
您可以通过list-style-image
属性使用图像作为标记。清单 24-9 展示了这个属性的实际应用。
清单 24-9。使用图像作为列表标记
`
I also like:
- bananas
- mangoes
- cherries
- plums
- peaches
- grapes
`
你可以在图 24-10 中看到应用该属性的效果。
图 24-10。使用图像作为列表标记
定位标记
您可以使用list-style-position
属性指定标记相对于li
元素内容框的位置。允许的值是inside
(表示标记在内容框内)和outside
(表示标记在内容框外)。清单 24-10 显示了使用中的list-style-position
属性及其值。
清单 24-10。指定标记的位置
`
I also like:
These are the inside items:
- bananas
- mangoes
- cherries
These are the outside items:
- plums
- peaches
- grapes
`
我将li
项分成了两个类,并应用了不同的list-style-position
属性值。你可以在图 24-11 中看到效果。
图 24-11。定位标记
在这个图中,我已经为所有的li
元素设置了background-color
属性,这样您就可以看到每个位置值的效果。
样式化光标
属性允许你改变光标的外观。表 24-6 总结了这一要素。
当鼠标经过样式化的元素时,cursor
属性的不同值导致浏览器显示不同样式的光标。您可以在清单 24-11 中看到正在使用的属性。
清单 24-11。使用光标属性
`
There are lots of different kinds of fruit - there are over 500
varieties of banana alone. By the time we add the
countless types of apples, oranges, and other well-known fruit, we are faced
with thousands of choices.
`
你可以在图 24-12 中看到效果。我已经放大了光标,以显示当我将鼠标放在span
元素上时,它会切换到 Windows 7 等待光标。
图 24-12。设置光标样式
总结
在这一章中,我描述了不适合其他地方的 CSS 属性。这并不是说这些属性不重要,只是它们不符合前几章的主题。本章中的属性允许您设置所有元素的颜色和不透明度,并对列表和表格应用特定的样式,这本身就是基本的 HTML 功能。
二十五、上下文中的 DOM
在本书的这一部分,您将探索文档对象模型(DOM)。到目前为止,您可以使用我向您展示的元素和 CSS 属性实现一些复杂的效果,但是如果您想要完全控制您的 HTML,您需要使用 JavaScript。DOM 是 JavaScript 和 HTML 文档内容之间的连接。使用 DOM,您可以添加、删除和操作元素。您可以使用事件来响应用户交互,并且您可以完全控制 CSS。
从这一点开始,你处于 HTML5 的编程端。到目前为止,您已经使用元素和 CSS 声明创建了内容,但是是时候戴上程序员的帽子开始使用 JavaScript 了。如果你需要复习,第五章给出了 JavaScript 基础知识的一个浏览。
了解文档对象模型
DOM 是表示 HTML 文档中元素的对象集合。名字说明了一切:DOM 实际上是一个模型,它由代表您的文档的对象组成。DOM 是 web 开发中的一个关键工具,它在 HTML 文档的结构和内容与 JavaScript 之间架起了一座桥梁。举例来说,清单 25-1 显示了一个简单的 HTML 文档。
清单 25-1。一个简单的 HTML 文档
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the` ` variety available in each country. I live near London, in an area which is known for its apples.
`你可以在图 25-1 中看到浏览器是如何显示样本 HTML 文档的。
图 25-1。显示基本的 HTML 文档
作为显示 HTML 文档过程的一部分,浏览器解析 HTML 并创建一个模型。该模型保留了 HTML 元素的层次结构,如图 25-2 所示,每个元素由一个 JavaScript 对象表示。
图 25-2。HTML 文档中元素的层次结构
正如您将在接下来的章节中看到的,您可以使用 DOM 来获取关于文档的信息或对其进行修改。这是现代 web 应用的基础。
模型中的每个模型对象都有属性和方法。当您使用这些来更改对象的状态时,浏览器会在相应的 HTML 元素中反映这些更改,并更新您的文档。
所有表示元素的 DOM 对象都支持相同的基本特性。这些是HTMLElement
对象,由HTMLElement
定义的核心功能总是可用的,不管对象代表哪种元素。此外,一些对象定义了额外的功能,允许您执行反映特定 HTML 元素独特特征的操作。我在第三十一章中描述了这些额外的特性。这是需要注意的重要一点:文档模型中代表一个元素的每个对象至少支持的的HTMLElement
特性,在某些情况下,还支持额外的特性。
并非所有可用的对象都代表 HTML 元素。正如您很快会看到的,有表示元素集合的对象,有表示关于 DOM 本身的信息的对象,当然还有Document
对象,它是我们进入 DOM 的门户,也是第二十六章的主题。
注意我在这里跳过了一些细节。如果您熟悉面向对象编程的概念,那么知道HTMLElement
是由 DOM 中包含的对象实现的接口可能会有所帮助。用于表示更具体元素的对象是从HTMLElement
派生的接口,这意味着您可以将对象视为实现或HTMLElement
,或其更具体的子类型。如果你不熟悉面向对象的概念,也不用担心。对于主流 web 编程来说,理解它们并不重要。我不会再提到它们,为了简单起见,我将把所有东西都称为对象。
了解 DOM 级别和合规性
当您开始使用 DOM 时,您会遇到提到 DOM 级别的 web 文章和教程(例如,某个特性是由 DOM Level 3 定义的)。DOM 级别是标准化过程的版本号,在大多数情况下,您应该忽略它们。
DOM 的标准化过程有好有坏。有描述每个 DOM 级别的标准和文档,但是它们并没有完全实现,浏览器只是简单地挑选有用的特性,而忽略了其他的。更糟糕的是,实现的特性之间存在一定程度的不一致。
部分问题是 DOM 规范是与 HTML 标准分开开发的。HTML5 试图通过包含一组应该实现的核心 DOM 特性来解决这个问题。然而,这尚未生效,分裂现象依然存在。
有许多方法可以处理 DOM 特性的可变性。第一种是使用 JavaScript 库,比如 jQuery,它消除了浏览器实现之间的差异。使用库的优点是一致性,但缺点是受限于库支持的特性。如果你想跳出这个库的特性,你就要直接使用 DOM,无论如何都会面临同样的问题。(这并不是说 jQuery 及其替代品没有价值;它们非常有用,非常值得一看。)
第二种方法是保守主义:只使用你知道被广泛支持的特性。这在很大程度上是最明智的方法,尽管它需要仔细彻底的测试。此外,你必须小心测试新版本的浏览器,以确保对功能的支持没有改变或删除。
测试 DOM 特性
第三种方法是测试 DOM 对象上与某个特性相关联的属性或方法是否存在。清单 25-2 包含一个简单的例子。
提示不要担心清单 25-2 中脚本的细节。我将在接下来的章节中解释它使用的所有对象和特性。
清单 25-2。功能测试
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
for (var i = 0; i < images.length; i++) {
images[i].style.border = “thick solid black”;
images[i].style.padding = “4px”;
}
在这个例子中,脚本使用一个if
子句来确定document
对象是否定义了一个名为querySelectorAll
的方法。如果子句评估为true
,那么浏览器支持该特性,我可以继续使用它。如果该子句评估为false
,那么我可以采取另一种方法来实现相同的目标。
当涉及到 DOM 时,这是你经常会看到的建议,但它通常是圆滑地给出的,没有指出缺点,这可能是严重的。
第一个缺点是,并不总是有替代方法来实现给定功能的效果。我在清单 25-2 中的简洁例子是有效的,因为我测试的特性是建立在其他函数之上的便利性增强,但情况并非总是如此。
第二个缺点是,我只测试特性的存在,而不是其实现的质量和一致性。许多特性,尤其是当它们是新特性时,需要几个浏览器版本来稳定和实现一致性。这已经不是以前的问题了,但是由于浏览器实现您所依赖的特性的方式不同,您很容易得到意想不到的结果。
第三个缺点是你必须测试你所依赖的每一个特性。这需要极度的勤奋,产生的代码充满了无尽的测试。这并不是说这不是一个有用的技术,但它有缺陷,不应该作为适当测试的替代品。
DOM 快速参考
以下部分提供了我在后面章节中描述的对象、方法、属性和事件的快速参考。
文档成员
第二十六章描述了Document
对象,它代表当前文档,是你进入 DOM 的门户。表 25-1 总结了该对象定义的成员。
第二十六章也描述了Location
对象,汇总在表 25-2 中。
窗口成员
第二十七章描述了Window
对象,它定义了广泛的特性。表 25-3 总结了该对象定义的成员。
第二十七章也描述了History
对象,其成员汇总在表 25-4 中。
第二十七章也描述了Screen
对象,其成员汇总在表 25-5 中。
html element 成员
第二十八章描述了HTMLElement
对象,它代表了文档中的 HTML 元素。表 25-6 总结了该对象定义的成员。
***表 25-6。*html element 对象
第二十八章也描述了Text
对象,它用来表示文档中的文本内容。表 25-7 描述了Text
对象的成员。
DOM CSS 属性
第二十九章描述了如何使用 DOM 来处理文档中的 CSS 样式。在表 25-8 中列出了CSSStyleDeclaration
对象的属性及其对应的样式(以及描述它们的章节)。
DOM 事件
第三十章讲解 DOM 事件系统。有许多不同的事件可用,如表 25-9 中所述。
总结
在这一章中,我已经提供了一些 DOM 的上下文以及它在 HTML 文档中扮演的角色。我还解释了 DOM 规范级别与主流浏览器实现的特性之间的关系,以及确保您所依赖的 DOM 特性在目标浏览器中可用的不同方法。虽然,必须指出的是,这些方法中没有一个可以取代勤奋和彻底的测试。
这一章还包括一些我在后面章节中描述的对象、成员和事件的快速参考表。
二十六、使用文档对象
在这一章中,我将向您介绍 DOM 的关键组件之一:Document
对象。Document
对象是 DOM 功能的入口,它为您提供了关于当前文档的信息和一组特性,用于浏览、导航、搜索和操作结构和内容。表 26-1 对本章进行了总结。
你通过全局变量document
访问Document
对象;这是浏览器为我们创建的关键对象之一。Document
对象为您提供关于整个文档的信息,并允许您访问模型中的单个对象。开始使用 DOM 的最好方式是通过一个例子。清单 26-1 展示了上一章的示例文档,并添加了一个演示一些基本 DOM 特性的脚本。
清单 26-1。使用文档对象
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
**** `这个脚本简短而简单,但是它巧妙地抓住了 DOM 的许多不同用途。我将把脚本分解成几个部分,并解释发生了什么。我们可以用Document
对象做的最基本的事情之一是获取我们正在处理的 HTML 文档的信息。脚本中的第一行就是这样做的:
document.writeln("<pre>URL: " + **document.URL**);
在本例中,我已经读取了document.URL
属性的值,它返回当前文档的 URL。这是浏览器用来加载运行脚本的文档的 URL。在本章后面的“从文档中获取信息”一节中,我将向您展示您可以从Document
对象中获取的不同信息。
该语句还调用了writeln
方法:
**document.writeln(**"<pre>URL: " + document.URL**);**
该方法将内容追加到 HTML 文档的末尾。在本例中,我已经编写了一个pre
元素的开始标记和URL
属性的值。这是一个非常简单的修改 DOM 的例子,意味着我改变了文档的结构。我在第二十八章中详细描述了 DOM 的操作。
接下来,我从文档中选择一些元素:
var elems = document.**getElementsByTagName("p");**
有一系列选择元素的方法,我将在本章后面的“获取 HTML 元素对象”一节中解释。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
元素意味着由writeln
方法添加的行尾字符将用于构建内容。你可以在图 26-1 中看到文件显示的效果。
图 26-1。脚本对基本 HTML 文档的影响
使用文档元数据
正如我在上一节中解释的那样,Document
对象的一个用途是为您提供关于文档的信息。表 26-2 描述了可以用来获取文档元数据的属性。
从文档中获取信息
您可以使用元数据属性获得一些关于文档的有用信息,如清单 26-2 所示。
清单 26-2。使用文档对象获取元数据
`
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(“”);
这些属性为您所处理的文档提供了一些有用的信息。您可以在图 26-2 中看到浏览器显示的这些属性的值。
图 26-2。关于文档的基本信息
理解怪癖模式
属性告诉你浏览器如何处理文档中的内容。世界上有很多不标准的 HTML,浏览器试图显示这样的页面,即使它们不符合 HTML 规范。其中一些内容依赖的功能可以追溯到浏览器竞争其独特差异的时代,而不是标准合规性。compatMode
属性将返回两个值中的一个,如表 26-3 所述。
使用位置对象
document.location
属性返回一个Location
对象,为您提供关于文档地址的细粒度信息,并允许您导航到其他文档。表 26-4 描述了Location
对象的功能和属性。
属性最简单的用途是获取当前对象的位置信息,如清单 26-3 所示。
清单 26-3。使用 Location 对象获取关于文档的信息
`
document.writeln("protocol: " + document.location.protocol);
document.writeln("host: " + document.location.host);
document.writeln("hostname: " + document.location.hostname);
document.writeln("port: " + document.location.port);
**document.writeln("pathname: " + document.location.pathname); **
document.writeln("search: " + document.location.search);
document.writeln("hash: " + document.location.hash);
document.write(“”);
search
属性返回 URL 的查询字符串部分,hash 属性返回 URL 片段。图 26-3 显示了 URL [
titan/listings/example.html?query=apples#apples](http://titan/listings/example.html?query=apples#apples)
的Location
属性返回的值。
图 26-3。使用位置对象获取信息
提示注意,当端口为 HTTP 的默认值)时,该属性不返回值。
使用位置对象导航到别处
您还可以使用通过document.location
属性获得的Location
对象导航到其他地方。有几种不同的方法可以做到这一点。首先,你可以给我在前面的例子中使用的属性赋值,如清单 26-4 所示。
清单 26-4。通过为位置属性分配新值来导航到文档
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
**Press Me**One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
`
这个例子包含了一个button
元素,当点击这个元素时,会给document.location.hash
属性分配一个新值。按钮和点击时将执行的 JavaScript 函数之间的关联是通过使用事件实现的。这就是onclick
属性的用途,你可以在第三十章中了解更多事件。
这种变化导致浏览器导航到其id
属性值与hash
值匹配的元素,在本例中是img
元素。你可以在图 26-4 中看到这种导航的效果。
图 26-4。使用位置对象导航
虽然我导航到了同一文档中的不同位置,但是您也可以使用Location
对象的属性导航到其他文档。然而,这通常是通过href
属性完成的,因为您可以设置完整的 URL。您也可以使用Location
对象定义的方法。
assign
和replace
方法的区别在于replace
从浏览器的历史记录中删除当前文档,这意味着当用户点击后退按钮时,浏览器将跳过当前文档,就好像它从未被访问过一样。清单 26-5 展示了assign
方法的使用。
清单 26-5。使用位置对象的分配方法导航
`
当用户单击button
元素时,浏览器将导航到指定的 URL,在本例中是[
apress.com](http://apress.com)
。
读写饼干
属性允许您读取、添加和更新与文档相关联的 cookies。清单 26-6 给出了一个演示。
清单 26-6。读取和创建 cookie
`
Add Cookie
Update Cookie
var cookieCount = 0;
document.getElementById(“update”).onclick = updateCookie;
document.getElementById(“write”).onclick = createCookie;
readCookies();
function readCookies() {
document.getElementById(“cookiedata”).innerHTML = document.cookie;
}
function createCookie() {
cookieCount++;
document.cookie = “Cookie_” + cookieCount + “=Value_” + cookieCount;
readCookies();
}
function updateCookie() {
document.cookie = “Cookie_” + cookieCount + “=Updated_” + cookieCount;
readCookies();
}
属性的工作方式有点奇怪。当您读取属性的值时,您将获得与该文档相关联的所有 cookies。Cookies 是形式为name=value
的名称/值对。如果有多个可用的 cookie,则所有 cookie 都作为 cookie 属性的结果返回,用分号分隔,例如:name1=value1;name2=value2
。
相比之下,当您想要创建一个新的 cookie 时,您可以分配一个新的名称/值对作为属性cookie
的值,它将被添加到文档的 cookie 集合中。一次只能设置一个 cookie 。如果您设置的值的名称部分对应于现有的 cookie,那么值部分将用于更新 cookie。
为了演示这一点,清单包含一个读取、创建和更新 cookies 的脚本。readCookies
函数读取document.cookie
属性的值,并将结果设置为段落(p
)元素的内容。
文档中有两个button
元素。当点击添加 cookie 按钮时,createCookie
函数为cookie
属性分配一个新值,该值将被添加到 Cookie 集合中。更新 Cookie 按钮导致调用updateCookie
功能。该函数为现有的 cookie 提供一个新值。你可以在图 26-5 中看到这个脚本的效果,但是要真正感受一下发生了什么,我建议你加载这个文档并四处看看。
图 26-5。添加和更新 cookie
在本例中,我添加了三个 cookies,其中一个已经更新为新值。虽然name=value
表单是添加 cookie 的默认表单,但是您可以应用一些额外的数据来改变 cookie 的处理方式。这些增加在表 26-5 中描述。
这些附加项中的每一项都被添加到名称/值对的前面,并用分号分隔,如下所示:
document.cookie = "MyCookie=MyValue;max-age=10";
了解就绪状态
属性给出了关于加载和解析 HTML 文档过程中当前阶段的信息。记住,默认情况下,浏览器一遇到文档中的script
元素就执行你的脚本,但是可以使用defer
属性推迟脚本的执行(如第七章所述)。正如你已经在一些例子中看到的,我将在第三十章中详细解释,你可以使用 JavaScript 事件系统来执行单独的功能,以响应文档或用户动作的变化。
在所有这些情况下,了解浏览器在加载和处理 HTML 时所处的阶段是很有用的。readyState
属性返回三个不同的值,在表 26-6 中有描述。
随着浏览器加载和处理文档,readyState
属性的值从loading
移动到interactive
再到complete
。该属性与每次readyState
属性的值改变时触发的readystatechange
事件结合使用最有用。我将在第三十章中解释事件,但是清单 26-7 展示了如何使用事件和属性来完成一个共同的任务。
清单 26-7。使用文档就绪状态推迟脚本执行
`
这个脚本使用文档就绪状态来推迟函数的执行,直到文档到达interactive
阶段。这个脚本依赖于能够在文档中找到在脚本执行时浏览器还没有加载的元素。通过推迟执行直到文档被完全加载,我可以确信元素会被找到。这是将script
元素放在文档末尾的替代方法。我将在本章后面的“获取 HTML 元素对象”一节中解释如何查找元素。我在第三十章中解释如何使用事件。
获取 DOM 实现细节
属性为您提供了关于 DOM 特性的浏览器实现的信息。该属性返回一个DOMImplementation
对象,其中有一个您感兴趣的方法:hasFeature
方法。您可以使用这种方法来确定实现了哪些 DOM 特性,如清单 26-8 所示。
清单 26-8。使用 document . implementation . has feature 方法
`
var features = [“Core”, “HTML”, “CSS”, “Selectors-API”];
var levels = [“1.0”, “2.0”, “3.0”];
document.writeln(“
”);”)
for (var i = 0; i < features.length; i++) {
document.writeln("Checking for feature: " + features[i]);
for (var j = 0; j < levels.length; j++) {
document.write(features[i] + " Level " + levels[j] + “: “);
document.writeln(document.implementation.hasFeature(features[i],
levels[j]));
}
}
document.write(”
`
这个脚本检查一些不同的 DOM 特性和定义的特性级别。这并不像看起来那么有用。首先,浏览器并不总是正确地报告它们实现的特性。一些实现了特性,但是没有通过hasFeature
方法报告它们,而另一些声称实现了特性,但是没有。第二,一个浏览器报告一个特性并不意味着它以一种有用的方式实现了。这已经不是什么大问题了,但是 DOM 实现之间还是有一些差异。
如果你打算写能在所有主流浏览器上工作的代码(你应该这样做),那么hasFeature
方法就没什么用了。相反,在测试阶段彻底检查您的代码,在需要时测试支持和回退,并且可选地,考虑使用 JavaScript 库(比如 jQuery),这可以帮助消除 DOM 实现中的差异。
获取 HTML 元素对象
对象的关键功能之一是充当代表文档中元素的对象的网关。您可以用几种不同的方式来执行这项任务。有一些属性可以返回表示文档中特定类型元素的对象,有一些方便的方法可以让您使用搜索标准来匹配元素,并且您可以将 DOM 视为一棵树并在它的结构中导航。在接下来的小节中,我将介绍这些技术。
提示显然,你想获得这些物品是为了用它们做有趣的事情。我将在第三十八章中描述如何使用这些对象,其中我描述了HTMLElement
对象的特性。
使用属性获取元素对象
Document
对象为您提供了一组属性,这些属性返回表示文档中特定元素或元素类型的对象。这些特性总结在表 26-7 中。
在表 26-7 中描述的大多数属性返回一个HTMLCollection
对象。这是 DOM 表示代表元素的对象集合的方式。清单 26-9 展示了访问集合中包含的对象的两种方法。
清单 26-9。使用 HTMLCollection 对象
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
var elems = document.images;
for (var i = 0; i < elems.length; i++) {
resultsElement.innerHTML += "Image Element: " + elems[i].id + “\n”;
}
var srcValue = elems.namedItem(“apple”).src;
resultsElement.innerHTML += "Src for apple element is: " + srcValue + “\n”;
`
使用HTMLCollection
的第一种方法是把它当作一个数组。属性返回集合中的项目数,并且支持标准的 JavaScript 数组索引器(?? 符号)来提供对集合中各个对象的直接访问。这是我在示例中使用的第一种方法,使用了document.images
属性来获取一个包含表示文档中所有img
元素的对象的HTMLCollection
。
提示注意,我使用了innerHTML
属性来设置pre
元素的内容。我将在第三十八章中更详细地解释这个属性。
第二种方法是使用namedItem
方法,该方法返回集合中具有指定的id
或name
属性值的项目(如果有的话)。这是我在示例中使用的第二种方法,其中我使用了namedItem
方法来检索表示具有id
属性值apple
的img
元素的对象。
提示注意,我读取了其中一个对象的src
属性值。这是一个由用于表示img
元素的HTMLImageElement
对象实现的属性。我在第三十一章中对这种物体做了更多的解释。我使用的另一个属性id
是HTMLElement
的一部分,因此可用于所有类型的元素。
使用数组符号获得一个命名元素
您还可以使用数组样式的符号来获得表示一个名为的元素的对象。这是一个具有id
或name
属性值的元素。清单 26-10 提供了一个例子。
清单 26-10。获取命名元素对象
`
` ` There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
if (elems.namedItem) {
for (var i = 0; i < elems.length; i++) {
resultsElement.innerHTML += "Image Element: " + elems[i].id + “\n”;
}
} else {
resultsElement.innerHTML += "Src for element is: " + elems.src + “\n”;
}
您可以看到我是如何使用数组风格的索引器来获得一个对象,该对象表示具有apple
的id
值的元素。以这种方式获取元素的一个奇怪之处在于,根据文档的内容和元素的顺序,可以得到不同种类的结果。
浏览器以深度优先的顺序查看文档中的所有元素,尝试将id
或name
属性与指定值匹配。如果第一个匹配是一个id
属性,那么浏览器停止搜索(因为id
值在文档中必须是惟一的)并返回一个代表匹配元素的HTMLElement
。
如果第一个匹配是针对一个name
属性值,那么您将收到一个HTMLElement
(如果只有一个匹配元素)或者一个HTMLCollection
(如果有多个)。一旦浏览器开始匹配name
值,它就不会匹配id
值。
您可以看到我如何使用namedItem
属性作为测试,以查看我收到了哪种结果。在这个例子中,我收到了一个HTMLElement
,因为我指定的值与一个id
值匹配。
提示你也可以将命名元素称为属性。所以,比如说,document[apple]
和document.apple
的意思是一样的。我倾向于使用点符号格式,因为这样可以更清楚地表明我在尝试获取元素对象,但这是个人喜好的问题。
搜索元素
Document
对象定义了许多方法,可以用来在文档中搜索元素。这些方法在表 26-8 中描述。
如您所料,其中一些方法会返回多个元素。我已经将这些表示为返回表中的一组HTMLElement
对象,但严格来说这并不正确。事实上,这些方法返回一个NodeList
,它是底层 DOM 规范的一部分,处理通用的结构化文档格式,而不仅仅是 HTML。然而,出于这些目的,您可以像对待数组一样对待它们,并将重点放在 HTML5 上。
搜索方法可以分为两类。清单 26-11 展示了这些类别中的第一个——那些名称以getElement
开头的方法。
清单 26-11。使用 document.getElement方法*
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
var fruitsElems = document.getElementsByClassName(“fruits”);
resultsElement.innerHTML += “There are " + fruitsElems.length
+ " elements in the fruits class\n”;
var nameElems = document.getElementsByName(“apple”);
resultsElement.innerHTML += “There are " + nameElems.length
+ " elements with the name ‘apple’”;
这些方法正如您所期望的那样工作,并且只有一个行为需要注意。当使用getElementById
方法时,如果没有找到具有指定的id
值的元素,浏览器将返回null
。相比之下,其他方法将总是返回一个HTMLElement
对象的数组,但是length
属性将返回0
来表示没有匹配。
使用 CSS 选择器搜索
一个有用的替代方法是使用 CSS 选择器进行搜索。选择器允许您在文档中找到更大范围的元素。我在第十七章和第十八章中描述了 CSS 选择器。清单 26-12 演示了用这种方式获取元素对象。
清单 26-12。使用 CSS 选择器获取元素对象
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples, oranges, and other well-known fruit, we are faced with thousands of choices.
One of the most interesting aspects of fruit is the variety available in each country. I live near London, in an area which is known for its apples.
var elems = document.querySelectorAll(“p, img#apple”)
resultsElement.innerHTML += “The selector matched " + elems.length
+ " elements\n”;
在这个例子中,我使用了一个选择器来匹配所有的p
元素和一个id
值为apple
的img
元素。使用其他文档方法很难达到同样的效果,我发现我使用选择器比使用getElement
方法更频繁。
将搜索链接在一起
一个很好的 DOM 特性是除了一个搜索方法之外,Document
对象实现的所有搜索方法都由HTMLElement
对象实现,允许你将搜索链接在一起。唯一的例外是getElementById
方法,它只能通过Document
对象使用。清单 26-13 展示了链式搜索。
清单 26-13。将搜索链接在一起
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples,
var elems = document.getElementById(“tblock”).getElementsByTagName(“span”);
resultsElement.innerHTML += “There are " + elems.length + " span elements\n”;
var elems2 = document.getElementById(“tblock”).querySelectorAll(“span”);
resultsElement.innerHTML += “There are " + elems2.length
+ " span elements (Mix)\n”;
var selElems = document.querySelectorAll(“#tblock > span”);
resultsElement.innerHTML += “There are " + selElems.length
+ " span elements (CSS)\n”;
在这个例子中有两个链式搜索,这两个搜索都是从getElementById
方法开始的(它给了我一个单独的对象来处理)。在第一个例子中,我使用了getElementsByTagName
方法链接搜索,在第二个例子中,我通过querySelectorAll
方法使用了一个非常简单的 CSS 选择器。每个例子都返回包含在p
元素中的span
元素的集合,该元素的id
是tblock
。
当然,您可以使用仅应用于Document
对象的 CSS 选择器方法来实现相同的效果(我已经在示例的第三部分展示了这一点),但是当您处理由脚本中的另一个函数(或第三方脚本)生成的HTMLElement
对象时,这个特性会很方便。你可以在图 26-6 中看到搜索的结果。
图 26-6。将搜索链接在一起
导航 DOM 树
搜索元素的另一种方法是将 DOM 视为一棵树,并浏览它的层次结构。所有 DOM 对象都支持一组属性和方法,让我们可以做到这一点;它们在表 26-9 中有描述。
清单 26-14 展示了一个脚本,让你在文档中导航,在pre
元素中显示当前选中元素的信息。
清单 26-14。导航 DOM 树
`
There are lots of different kinds of fruit - there are over 500 varieties of banana alone. By the time we add the countless types of apples,
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 == “parent” && element != document.body) {
element = element.parentNode;
} else if (e.target.id == “child” && element.hasChildNodes()) {
element = element.firstChild;
} else if (e.target.id == “prev” && element.previousSibling) {
element = element.previousSibling;
} else if (e.target.id == “next” && element.nextSibling) {
element = element.nextSibling;
}
processNewElement(element);
if (element.style) {
element.style.backgroundColor = “lightgrey”;
}
}
function processNewElement(elem) {
resultsElem.innerHTML = "Element type: " + elem + “\n”;
resultsElem.innerHTML += "Element id: " + elem.id + “\n”;
resultsElem.innerHTML += "Has child nodes: "
+ elem.hasChildNodes() + “\n”;
if (elem.previousSibling) {
resultsElem.innerHTML += ("Prev sibling is: "
+ elem.previousSibling + “\n”);
} else {
resultsElem.innerHTML += “No prev sibling\n”;
}
if (elem.nextSibling) {
resultsElem.innerHTML += "Next sibling is: "
+ elem.nextSibling + “\n”;
} else {
resultsElem.innerHTML += “No next sibling\n”;
}
}
脚本的重要部分用粗体显示;这是进行实际导航的部分。脚本的其余部分处理设置、处理按钮点击和显示关于当前所选元素的信息。你可以在图 26-7 中看到脚本的效果。
图 26-7。导航 DOM 树
总结
在这一章中,我向您介绍了Document
对象,它是浏览器为您创建的,充当进入文档对象模型(DOM)的网关。我解释了如何获取关于文档的信息,如何查找和获取表示文档中元素的对象,以及如何将 DOM 作为树结构导航。