早在99年7月,我就在邮件列表中鼓吹样式表(style sheets)的种种优越之处。看来有些事情永远都不会改变。
而改变了的,是我对CSS的看法,以及它所应用于(X)HTML的底层结构。例如,我发现大多数的网页在导航区都包含一个链接的菜单。它们通常被编辑成一串链接,常常各自以层(div)或段落的方式来标记(markup)。然而,从结构上讲,它们是一个链接列表,并且也应该用列表来标记。
当然,之所以不这样标记是因为我们不希望在导航区的每一个链接的前都出现项目符号(bullet)。在上一篇文章中,我概述了用CSS来为网页布局的一些技术。其中之一就是如何将竖直方向显示的列表处理为水平方向显示。
在本文中,我将演示如何利用CSS使笨拙的列表得到有效的控制。现在该是你向列表发号施令,告诉它该怎样去做的时候了,而不是让它们在你的网页上面随意运行。
搭建舞台
为了达到本文的目的,我将使用无序列表。应用CSS也同样可以使其变得如同有序列表般井井有条。除非有其他的定义,否则在本文的示例示例中,列表都将使用如下代码:
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> <li>Item 4</li> <li>Item 5 we'll make a bit longer so that it will wrap</li> </ul>
每个列表都将被置于不同的层中,并且在CSS中加以标记,因此,列表的行为由其所在的层来决定。每个层都应用一条基本规则:
#base { border: 1px solid #000; margin: 2em; width: 10em; padding: 5px; }
如果没有加以额外的样式,在基本层(base DIV)中,列表应该是这样被渲染的:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
位置
有时列表默认的缩进对于你所进行的设计来说太多了。但是仅仅改变 UL 的外补丁(margin)或内补丁(padding)并不能保证在所有的浏览器中都正常显示。比如,为了使列表左对齐,你必须同时改变它的内外补丁。这是因为 IE 和 Opera 倾向于用左外补丁来创建缩进,而 Mozilla/Netscape 则是用内补丁。关于缩进的更多内容可以参阅 DevEdge 的文章 整齐的列表缩进。
在下面的例子中,层中 UL 的左侧内外补丁都被设置为零:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
注意项目符号(marker)位于层之外,如果容器是 HTML 文档的主体(body),那么项目符号就有可能被渲染到窗口之外,消失不见了。如果你希望项目符号出现在层内,但是又要单独留在左侧的话,就将左侧的内补丁或者外补丁设置为1em:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
项目符号
或许在你的项目中需要自定义的项目符号。如果是这样的话,你可以在表格中创建一列,专门用来容纳 GIF 格式的项目符号图片,并将它们设置为右上方对齐,并在另一个栏目中填入本应在 LI 中出现的内容。有了 CSS,我们就可以使用图像来作为项目符号。如果浏览器不支持 CSS 的这个部分(或者不支持图像),则会显示默认的项目符号(或者定义为你所想要的 HTML 项目符号)。
这一规则就像下面这样:
ul { list-style-image: url(bullet.gif); }
浏览器这样渲染它:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
如果浏览器不支持 CSS 的这部分,那么可以为其指定一个 HTML 项目列表,在规则中添加:
list-style-type: disc;
你可能会发现,指定的项目符号和列表项的对齐方式并不是自己所期望的那样,当然,这与你选择的图片有关。在这种情况下,可以将图片置于列表项的块(list item block)之内(而不是块的外部)。加入如下的的规则就可以实现:
list-style-position: inside;
以上的三个声明可以合并为一个缩略声明,如下所示:
ul { list-style: disc url(bullet.gif) inside; }
等价于:
ul { list-style-type: disc; list-style-image: url(bullet.gif); list-style-position: inside; }
网页中所看到的就是下面这样:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
有时你可能只想要列表,不想要项目符号,或者希望用一些其他的字符来代替项目符号。CSS 同样也提供了直接的解决办法。只需添加一条 list-style:none;
强制 LI 以悬挂式缩进来显示。规则如下:
ul { list-style: none; margin-left: 0; padding-left: 1em; text-indent: -1em; }
需要将内外补丁的其中一个设置为零,另一个设置为 1em。根据所选的“项目符号”,这一值也应随之改变。将文字缩进(text-indent)设置为负值使得第一行向左移动你所设置的单位,创建出悬挂式缩进。
HTML 包含标准的 UL,但还可以用字符或 HTML 实体(HTML entity)来代替项目符号,使其出现在列表项的内容之前。在我们的例子中,将使用 »
,它是右书名号 » :
- » Item 1
- » Item 2
- » Item 3
- » Item 4
- » Item 5 we'll make a bit longer so that it will wrap
需要指出的是,在 Netscape6/7/Mozilla(以及其它一些基于 Gecko 内核的浏览器)和 Opera 中,可以通过 CSS2 中的 :before 伪元素 来创建生成内容。我们可以利用它来自动生成项目符号 »(或其它的字符)。这就允许我们单独处理 UL 的内容。如果你使用的是 Opera 或基于 Gecko 内核的浏览器,那么下面的 CSS 将创建出与上面相同的列表,但使用的是没有额外内容的标准 UL:
#custom-gen ul li:before { content: "\00BB \0020"; }
内容(content)属性中可能包括字符串,URI 以及其它一些元素,比如特殊字符。当你使用像 » 这样的字符时,很有必要将它们编码为与之等价的十六进制形式(HEX equivalents)。对于右书名号来说,我们使用 \00BB(其它的字符亦可,比如 \0020是空格)。最终结果是这样的(记住,这些字符只有在 Opera 或者 Mozilla/Netscape 中才是可见的):
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
真正的内嵌式列表
谁说列表一定要竖直对齐,并且列表项的前边都有项目符号?或许你想要是一个由顺序链接组成的列表结构,但是在看上去却是页面上的垂直导航栏。再或许你希望链接列表能够以水平对齐的方式出现在页面上方。
这不仅仅适用于链接列表。有时你可能需要在段落中插入项目列表,也许是你想阅读的书籍列表。从结构上讲,将它们标记为列表是有意义的,但是从表象上来说你可能并不希望破坏一个段落的流畅性。这时,CSS又要发挥作用了!
由于这个列表本身并不是孤立的,我将不会把它放到前面的列表所继承的基本层中去。这次的标记会是段落,紧接着有着同样的列表和另一个段落。
我听到你们抱怨:“犯规!本以为你会把列表放在段落内部,而不是夹在两个段落的之间。”
对于这样的抱怨,我的回答是:“是的,但是(X)HTML不允许列表出现在段落的内部,然而,在样式表的帮助下,可以让它看起来在段落内部。”
样式应该是如下这样的:
#inline-list { border: 1px solid #000; margin: 2em; width: 80%; padding: 5px; font-family: Verdana, sans-serif; } #inline-list p { display: inline; } #inline-list ul, #inline-list li { display: inline; margin: 0; padding: 0; color: #339; font-weight: bold; }
这一标记包含了一个层 <div id="inline-list">
。而层中又包含了段落,紧随其后的是标准 UL,再接下来是又一个段落。UL 列表经过修改后,每个列表项的后边都跟随有逗号,最后一个项目之后是句号。
结果如下所示(列表用蓝色粗体表示):
A bit of text before the list appears. Perhaps the context is something about a husband getting a call from his wife to pick up a few things on the way home from work. It doesn’t really matter, for our purposes we just need some preceding text before the list:
- Item 1,
- Item 2,
- Item 3,
- Item 4,
- Item 5 we'll make a bit longer so that it will wrap.
And then there is the text that follows the list in the paragraph. One or two sentences is sufficient for the example.
正如上面自定义项目符号的例子一样,我们可以用 CSS 来自动生成跟随在每个列表项之后的逗号和句号。如果你只关心 Opera 和基于 Gecko 内核的浏览器的话,那么这样就足够了。样式应该是这样的:
#inline-list-gen ul li:after { content: ", "; } #inline-list-gen ul li.last:after { content: ". "; }
这次我们使用了 :after 伪元素 来为每个列表项后添加逗号,而对于标记有 class="last"
的列表项,我们在它之后添加句号,结果如下(记住,只有在 Opera 或者 Mozilla/Netscape 中才可见):
A bit of text before the list appears. Perhaps the context is something about a husband getting a call from his wife to pick up a few things on the way home from work. It doesn’t really matter, for our purposes we just need some preceding text before the list:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
And then there is the text that follows the list in the paragraph. One or two sentences is sufficient for the example.
导航
正如我在前面提到的那样,几乎在所有的网页上都会出现的链接菜单本应被标记成列表,因为它们原本就是这样的。由于人们通常希望页面上的链接不以默认的列表样式出现,我们就可以利用 CSS 来改变它们的样式。像上面看到的那样,列表可以水平(内嵌)而非竖直(默认风格)地显示。当你开始做这一部分的时候,完全可以不用项目符号,将列表项分开的方式有很多种。
这些水平列表的示例都是基于基本层而应用样式的:
#h-contain { padding: 5px; border: 1px solid #000; margin-bottom: 25px; }
接下来的两个示例使用与前面相同的 UL,但最后一个列表项中并未含有额外的文字。它们还包括另一个类, 这个类使得列表中某一 LI 与其它分离。
边框
通道符号(pipe character) “|” 通常用于区分选项,它是一个明显的分隔符,并且可以通过为列表项添加边框的办法来模仿它:
#pipe ul { margin-left: 0; padding-left: 0; display: inline; } #pipe ul li { margin-left: 0; padding: 3px 15px; border-left: 1px solid #000; list-style: none; display: inline; } #pipe ul li.first { margin-left: 0; border-left: none; list-style: none; display: inline; }
这里我们为第一个 LI 添加 class="first"
,以使它不出现左边框。
- Item 1
- Item 2
- Item 3
- Item 4
通过修改样式,可以创建出标签导航栏:
#tabs ul { margin-left: 0; padding-left: 0; display: inline; } #tabs ul li { margin-left: 0; margin-bottom: 0; padding: 2px 15px 5px; border: 1px solid #000; list-style: none; display: inline; } #tabs ul li.here { border-bottom: 1px solid #ffc; list-style: none; display: inline; }
- Item 1
- Item 2
- Item 3
- Item 4
本例中,通过为 LI 添加类 class="here"
来使其下边框与背景颜色一致,指出这一标签是当前标签。
注释:这个技术最先由 Randal Rust 提供,并且在 CSS-讨论 邮件列表中被 多次重复 讨论。
面包屑式的轨迹
在页面中,另一种具有典型的水平方向性的链接列表就是我们所熟知的面包屑式。面包屑指示出在当前页面属于网站的哪一层次,从主页开始一直到当前页面。如果你真的希望的标记具有意义,那么可以创建一系列内嵌的列表来实现,因为每个版块都是前一版块的一部分:
<div id="bread"> <ul> <li class="first">Home <ul> <li>» Products <ul> <li>» Computers <ul> <li>» Software</li> </ul></li> </ul></li> </ul></li> </ul> </div>
创建出:
- Home
- » Products
- » Computers
- » Software
- » Computers
- » Products
在页面的样式表中添加如下规则:
#bread { color: #ccc; background-color: #006; padding: 3px; margin-bottom: 25px; } #bread ul { margin-left: 0; padding-left: 0; display: inline; border: none; } #bread ul li { margin-left: 0; padding-left: 2px; border: none; list-style: none; display: inline; }
创建出:
- Home
- » Products
- » Computers
- » Software
- » Computers
- » Products
同样,我们可以用 :before伪元素 来生成 » 字符(或者其它你希望作为分隔符的字符),结合类 class="first"
以使第一个 LI 不生成分隔符:
#bread-gen ul li:before { content: "\0020 \0020 \0020 \00BB \0020"; color: #ff9; } #bread-gen ul li.first:before { content: " "; }
最终结果:
- Home
- Products
- Computers
- Software
- Computers
- Products
现实世界
我想用一个现实中的应用来作结尾,这一应用涵盖了我们刚才讨论过的一些技术。我们将使用一个包含链接的标准 UL 来创建具有悬浮效果的动态菜单。为了实现悬浮效果,我们使用UL来提供结构,由锚点的样式来提供大部分可视化效果。
这个链接菜单实际上正是对 Michael Efford 在 css-讨论 邮件列表中提出的问题的解决方案。Michael已经利用 表格,图像,和JavaScript 精确地完成了这一效果。他 在列表中询问 这样的列表是否能由CSS来完成。我接受了挑战,并且在其他一些精通浏览器特殊细节的成员的帮助下,我们用以下的标记加上样式表实现了这种效果:
<div id="button"> <ul> <li><a href="#">Home</a></li> <li><a href="#">Hidden Cameras</a></li> <li><a href="#">CCTV Cameras</a></li> <li><a href="#">Employee Theft</a></li> <li><a href="#">Helpful Hints</a></li> <li><a href="#">F.A.Q</a></li> <li><a href="#">About Us</a></li> <li><a href="#">Contact Us</a></li> </ul> </div>
我们一条条地来看这些规则,我会解释它们为什么是这样构造的。
#button { width: 12em; border-right: 1px solid #000; padding: 0 0 1em 0; margin-bottom: 1em; font-family: 'Trebuchet MS', 'Lucida Grande', Verdana, Lucida, Geneva, Helvetica, Arial, sans-serif; background-color: #90bade; color: #333; }
第一条规则是对于#button DIV(层)而言的。它定义了菜单所需空间,并且足以容纳菜单,这样就可以可以定义列表和链接在层中行为方式。我选择使菜单可变,基于浏览器对字体大小的偏好,所以(几乎)所有的单位都是 em。也包括菜单的宽度。右侧的实心黑色边框的边界遵从了 Michael 的原始设计。下补丁是为了使层的下方扩展,以超过链接菜单的下边缘,这样你可以看到层的背景。同样,这也沿用了原始设计,底部的外补丁是为了将层与它之后的元素分开。颜色也来自与原始设计。
#button ul { list-style: none; margin: 0; padding: 0; border: none; } #button li { border-bottom: 1px solid #90bade; margin: 0; }
接着定义列表的外观。由于所有的列表项将来都是链接,并且实现滚动效果的代码是写在 CSS 中的,所以我基本上去掉了列表原有的样式。我的确在每个链接的下方都添加了1象素的边框,并使其颜色与层的背景相吻合,将它们用作分隔符。在原始设计中,此处用的是图像。
#button li a { display: block; padding: 5px 5px 5px 0.5em; border-left: 10px solid #1958b7; border-right: 10px solid #508fc4; background-color: #2175bc; color: #fff; text-decoration: none; width: 100%; } html>body #button li a { width: auto; } #button li a:hover { border-left: 10px solid #1c64d1; border-right: 10px solid #5ba3e0; background-color: #2586d7; color: #fff; }
最后,定义链接。在原始设计中,链接的左右各有10象素的边框。鼠标悬浮在链接上方时,边框与链接同时改变颜色。在CSS中通过 :hover伪类来控制这些是非常简单的。因此我将它们应用到锚点样式中。
在样式表的这部分中有一个工作区。为了使得链接的活动区域充满整个层,我将它们的显示属性设置为块(display:block;)。这对于除 IE/Windows 之外的浏览器都是有效的。如果你为块赋一个明确的宽度100%,那么 IE/Windows 也可以正常显示。但是这样做却为 IE5/Mac 和 Netscape/Mozilla 带来了问题。所以我用 子选项 “>” { 译者注:关于 子选项,在 CSS中的“滑动门”技术,第二部分 中同样进行了应用和探讨 } 将宽度重新定义为自动。由于 IE/Windows 不识别子选项,它会忽略这一规则。而 IE5/Mac, Opera 和 Netscape/Mozilla 遵循这一规则。这样一来就皆大欢喜了。
:hover伪类 的这一规则使得鼠标悬浮在链接上时,链接的背景色和边框颜色同时改变。
这些样式和列表标记(大约 1K)替代了大约5K的JavaScript和表格标记,更不用说滚动效果所需的大约 8K 左右的图像了。这同样也使标记更具可读性,更易于更新,因为当链接名称改变的时候你不必为它重新创建图片了。而只需修改文字即可。你可以在 Asset Surveillance 的页面上看到最终结果。
冰山一角
无论你是否相信,对于通过样式表能对列表所进行的修改,我们仅仅是浅尝辄止罢了。我并不强调本文所提及的是最实用的 CSS 技术,但是我的确希望它们能引发你去思考如何使用 CSS 来减轻工作负担并且使得标记更加结构化。
经 A List Apart 杂志 及作者许可翻译本文。
Translated with the permission of A List Apart Magazine and the author[s].