写这篇啰嗦的文章,缘由来自于Jorux Notebook的《什么是Semantics?》文章。
实际上,从《网站重构》一书开始在国内流行之后,Zeldman提出的语义化标记就已经在一定程度上流传开来。在抛弃table布局页面之后大家突然发现,原来这还不够,随意地使用<p>或者<div>来进行布局实际上和table布局一样的糟糕。
Jorux的例子(如下):
我是老大 | 我是不好意思说 | 老三 | 老四在这里 | 老幺
Jorux指出,在这种极为普遍的导航样式中(通常是用于网站底部的信息导航,100个网站里有90个采用这样的模式),我们应该使用无序列表,而不是下面这种:
1: <p>
2: <a href=”home.html”>首页</a> |
3: <a href=”about.html”>关于</a> |
4: <a href=”blog.html”>博客</a> |
5: <a href=”message.html”>留言</a> |
6: <a href=”album.html”>相册</a>
7: </p>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
甚至是使用了<ul>,但是依然将修饰线条作为内容元素的写法也是五十步笑百步:
1: <ul>
2: <li><a href=”home.html”>首页</a></li>
3: <li>|</li>
4: <li><a href=”about.html”>关于</a></li>
5: <li>|</li>
6: <li><a href=”blog.html”>博客</a></li>
7: <li>|</li>
8: <li><a href=”message.html”>留言</a></li>
9: <li>|</li>
10: <li><a href=”album.html”>相册</a></li>
11: </ul>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
遗憾的是,内容与样式分离web标准化建设的推进道路总是充满了各种各样的阻碍。即使是新浪和淘宝在它们的页面底部也存在着上述的情况。
先来看新浪的截图:
代码(简略了内容):
1: <div style="padding-bottom:6px;">
2: <a href="">新浪简介</a> ┊
3: <a href="">About Sina</a> ┊
4: <a href="">广告服务</a> ┊
5: <a href="">联系我们</a> ┊
6: <a href="">诚聘英才</a>
7: </div>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
为什么要这么做,有过网页制作经验的朋友大概会了解。网站底部的导航通常都是以比较简单的文本形式出现,居中,再附加一些修饰——大部分都是“|”和“·”或者“—”。html部分采用新浪的做法,节省了编写css的时间,最重要的是让一切水平居中显示将是非常简单的事情。只需要将<div>的text-align设置为center就万事大吉。
这里需要讨论的是,到底使用什么标记才算是语义化?
我们没有教科书,任何一本某某权威指南不会写着:你应该使用<ul>和<li>,而不是<div>和<p>来制作导航菜单。也就是说,我认为如果考虑到实际项目的操作,使用前者或后者都不是一个严重到会出现违背XHTML准则的问题。
我们来看淘宝的底部:
在我看到,如果按照zeldman的思路,应该这样去写:
1: <dl>
2: <dt>全球阿里巴巴 - 阿里巴巴网络</dt>
3: <dd>
4: <ul>
5: <li>中国站</li>
6: <li>国际站</li>
7: ......
8: </ul>
9: </dd>
10: </dl>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
但实际上,淘宝的代码是这样:
1: <div class="ali-group" style="width:680px;margin:5px auto;">
2: 全球阿里巴巴 - 阿里巴巴网络:
3: <a href="">中国站</a>
4: <a href="">国际站</a>
5: <a href="">日文站</a> |
6: <a href="">淘宝站</a> |
7: <a href="">支付宝</a> |
8: ......
9: </div>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
为什么一个如此提倡标准化的UED小组所编写的淘宝代码中也有和Jorux说法相违背,不严格按照语义化标记来操作呢?
因为,这样的水平居中和添加“|”不会那么烦人。
烦人的分割线
不要小看这个“|”,加入这么一个小小的修饰线条,看似简单,实际上还是比较头痛的。
首先我们知道,这个竖线是分割线,是把一个个链接从视觉上分开的一种手段,有多种方式去实现这一个效果:
1、直接插入“|”实体,但这和用空的table来撑开空白一样,是不符合内容和样式分离准则。
2、使用border-right来给每一个链接加上一个1px宽的边框。缺点是这个边框线条的高度定义起来比较讨厌,作为行内元素的a,无法应用到上下内边距来控制高度,勉强使用行高line-height只会让你在实际应用中漏洞百出。
3、使用background来给每一个链接加上一个竖线的图片背景,这种方式在视觉效果上应用灵活,但缺点是会多制作一张图片(尽管你可以CSS Sprites或者你认为一条竖线的图片大小可以忽略不计),同样高度无法定义。
4、使用:after这样的伪类元素,如a:after { content: "|"; }。可以再添加font-size来稍稍控制高度。尽管这样的一种做法被一些css玩家所推崇,毕竟了解的人少,使它看上去比较的高深和复杂。实际上它的缺点也最多。首先,:after在版本8以下的IE中通通免谈,这就意味着大部分浏览者无法看到这样的效果。其次,看上去这一条“|”是由css来控制的样式,实际上它在html中实际生成了内容,本质上和直接在<a>之后插入一个“|”没有区别。
综上所述,我们理应抛弃第一种做法,采用2、3、4中的一种来显示这个分割线效果。然而接踵而至的问题是,不管你用了哪种方法,你都要考虑如何去除最后一个“|”分割线。让导航看上是这样:
我是老大 | 我是不好意思说 | 老三 | 老四在这里 | 老幺
而不是这样:
我是老大 | 我是不好意思说 | 老三 | 老四在这里 | 老幺 |
常用的做法是给div中的最后一个a,或者ul中的最后一个li(或者都是第一个a和li,这取决与你css中的左右位置)添加一个id或者class,例如:
1: <ul id="menu">
2: ......
3: <li><a href="#">王老五</a></li>
4: <li class="last"><a href="#">老幺</a></li>
5: </ul> .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
然后在css添加对这个.last的单独定义,去除它的边框或者背景或者:after生成的content内容。
如此的繁琐,是不是已经开始头大了?
水平居中
从分割线的问题,我们往下讨论导航的水平居中问题。
淘宝的另一个底部导航采用了不让人头大的选择:
在这个例子中,HTMl如下:
1: <ul class="foot-nav" style="width:690px;_width:695px;">
2: <li><a href="">关于淘宝</a></li>
3: <li><a href="">广告服务</a></li>
4: <li><a href="">合作伙伴</a></li>
5: <li><a href="">帮助中心</a></li>
6: <li><a href="">诚征英才</a></li>
7: <li><a href="">联系我们</a></li>
8: <li><a href="">网站地图</a></li>
9: <li><a href="">热门品牌</a></li>
10: </ul>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
这里的采用了标准的写法,没有分割线,没有额外的id或class,没有上面讨论的烦人的各种问题。
然而这种写法意味着,如果将li的display设置block,text-align:center无法轻松的将这个导航水平放置在页面的正中,除非你给ul定义了一个宽度,在上面的代码中淘宝也的确是这样去做的。
如果,导航菜单中的项目数量不能够确定,整体的宽度势必也要成为变量——一个未知宽度。不断地去修改这个width,以确保margin: 0 auto;可以让这个导航水平居中无疑将让你的工作变得琐碎和困难。
未知宽度的水平居中
最简单的方法,就是让每一个li元素不要以block的形式float:left,以inline的方式让它们一字排开,给ul一个text-align:center就可以搞定这一切。
我做了这样一个例子,demo1。为了图省事,我没有做图,采用背景图片的方式显示分割线,而是使用了:after生成content的方法。
在css中的最后,我添加这样两行:
1: #menu li:before { content: "|"; padding-right: 10px; }
2: #menu li:first-child:before { content: ""; }
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }
这样,在不支持:after的浏览器中,导航以最质朴的方式展现,在IE8和FF这样的浏览器中,分割线将显示出来,最后一个li的分割线将被删除。
上面的方法,在大部分时间还是比较好用的,但如果需要给导航菜单添加更多的效果控制,例如给每一个li添加一个按钮似的背景,我们就必须让li成为块级元素。这个时候,text-align:center就无效了。
好在Stu Nicholls给出了一个解决方案——Centering Float Left Menus。
我做了另一个例子,demo2。
在这样的基础之上,无论你或删或增导航菜单的项目数量,无论你是想给它添加简单背景色,还是添加复杂的圆角框,你都可以从容应对了。