本文已经发表于InfoQ中文站,(
充满Trick的CSS,两难的选择)
javaeye的hax最近在他的blog上进行了一场关于如何写css的讨论,其中反思和讨论了一些关于基于标准或trick进行设计的选择问题,这个问题也是David Heinemeier Hansson对于XHTML/CSS/Javascript标准进行RIA开发话题的一个延展。我们可以从中思考如何在不完美的技术中选择一条相对完美的技术路线?
讨论的起因是淘宝网的UED团队成员段王爷在他的blog上发表了一篇关于淘宝网页面设计上的小技巧(Trick)的介绍,这个技巧是为了让一些条目之间的分隔线完全使用css生成,不使用多余的class,段王爷还对比了其它三种常见实现方法。实现方法如下:
从很久很久以前开始,类目间的横线无非都只有三种。
1、背景图
在a标签设置一个padding 用宽1px高不等的背景图来position到右侧。
缺点:最后一个还是要用class来隐藏掉背景。
2、符号
在每个a标签之间用“|”符号来填充。
缺点:html文件变大,文件维护变得很麻烦,而且在html中毫无意义。
3、a标签右侧的boder。
同背景图一样,只不过使用border-right来代替。缺点也同上。
其实现有(淘宝的实现方式)是利用ul的overflow:hidden 再将li的margin-left:-1px的做法做出来的。这样的做法就可以同时避免以上的缺点了。
上面提到的使用border的传统方式需要在第一或者最后一个元素上面添加class来隐藏border,Realazy也在他的blog中给出了一种不错的解决方案,他推荐这样做:
导航项目间的竖线,我们往往通过border或者background来实现。特殊需求是,第一项左边无竖线或最后一项右边无竖线。按照一般的编程方法,控制第一项要比控制最后一项容易得多。
区分第一项的还有一个好处是,CSS有一个:first-child的伪元素(pseudo element)可以让我们轻而易举的选择第一个子元素。遗憾的是,当前全球占有率最高的浏览器,IE6,并不支持这个伪元素。我们可以手工给第一个元素加上class然后再定义它。等ie6淘汰之时,就可以放心用 :first-child了,相权衡的话,我觉得给第一项加上class="first"也不失为实用主义做法。
Realazy提出的方案的思路是使用基于标准的css选择器(selector),这种方法的好处是可以实现完美的内容与表现分离。但是现实的问题是并非所有的浏览器都基于标准实现,这也正是基于标准的RIA开发面对的最大问题,尤其以CSS和Javascript问题最大。javaeye的hax在他的blog中提出了自己对于这个Trick的不同意见:
因为我觉得这个方法一点也不好。很简单的一个理由:这只是一个trick,只适合这特殊情况,假设你要换用“-”来分割呢?作为插入分割符号来说,真正合理使用css的,我给一个例子:
li ~ li:before { content:'-'; margin:0.25em; }
优点:含义非常清晰,维护性极高。你可以换任何的分隔字符,可以设定字体,可以设定颜色、大小等样式,甚至可以换用图片作为分隔。
好了,下面说缺点。唯一的缺点:IE不支持。
hax给出的方案在IE中无法使用,其实对于大部分网站来说这就相当于绝大多数用户都无法看到这种表现,这就意味着失败。淘宝UED的小马提出“实用第一”,从这种观点上说hax的方案就是不实用的。但是hax提出可以使用Dean Edwards的IE7 Javascript库:
IE7是一个可以让IE像标准浏览器一样工作的Javascript库。它修正了很多CSS问题,让PNG在IE5和IE6下正常工作。
IE7这个库动态的实现了很多IE原本不支持的伪类(Pseudo Classes),让完全基于标准的css选择器(使用伪类)成为可能。随后,hax在他的另外一篇blog“面向未来的CSS实践”中作了如下设想:
我推崇一种面向未来的CSS实践。即大胆采用CSS2.1甚至部分CSS3的特性。因为绝大多数特性,Firefox、Opera、 Safari等都已经很好的支持了。MSIE7也改进了许多,将来IE也无疑终究会完全支持CSS2.1。对于目前的IE,除了graceful degradation的方式(实际上整个内容样式分离的原则和良好的CSS设计可以确保这点,比如淘宝以前的“裸体”所体现的),可以考虑通过特定手段来patch之。
在这点上,我必须说,我原来也是一直坚持只用ie6的selector的。是什么改变了我?就是Dean Edwards的IE7!它的出现不仅在于实践价值——即提供了一个对于IE的补丁,让开发者可以直接写CSS2甚至CSS3。
hax提出的这种方式是比较激进的,他还在“面向未来的CSS实践”中进一步的描述了通过脚本修正的方式解决IE不支持标准伪类和多class问题的设想,他的核心想法就是让CSS的开发可以遵循标准,减少为了优雅退化(graceful degradation)而向最低支持(浏览器)兼容造成的表达方式限制。但是hax自己也提出了这种思路面临的尴尬,它举了table布局的实用性价值为例:
我认为出现这样讽刺的情况,即遵循标准的人活得比不遵循的人更累,是很有问题的。这种矛盾在我身上存在着,2001年的时候我在某bbs上发了个贴,大数 table布局之罪,但是过了几天我又跑上去说table布局在某种情况下也可以用用。 dlee同志貌似到现在也跟我当时一样。如果你确实认为,table布局从实用主义角度无法被完全否定,那DHH同志采用实用主义的角度来力挺 html/css/js就也有点心虚,那个标题也就显得带点任性味道……
“遵循标准的人活得比不遵循的人更累”这句话说出了很多坚持基于标准进行CSS设计的开发人员的心声,这其实是实用性和坚持标准之间的一些交换,现实世界中两个方面如何平衡正是广大XHTML/CSS/Javascript开发者需要认真思考的,关键的问题,还是目的要明确。盲目的遵循标准,例如很多开发者着迷于使用div布局代替table,但是却没有明确的目标就会迷失,hax这样评价:
从实用主义角度说,谨慎的table布局也许更简单,因为它更好的映射到了grid模型上。如果你转用div/span,标签是清晰了,但是css是混乱的!这些属性(css属性)是分散的,css代码无法反映整体,无法记录你的grid 布局意图!这是为什么我们经常说我有一个css trick的原因,它是trick而已,是你达到最终目的的手段,但是你的目的,你的意图,没有好好加入文档的话,那维护起来恐怕也不见得轻松。
table布局 其他css样式 = 清晰的布局意图和内容的混合体
div容器 css样式 = 内容样式分离,但是从css代码中很难看出布局意图
关于div/css布局还有一些误区,简单的把table标签换成div是没有意义的(若干层级的div可能比table更糟糕)。实际上我们希望的是语义标签。
我们应该看到,CSS的意图是将表现分离。从设计的角度就是实现语义化的html结构,让html/xhtml尽量只表达纯粹的数据结构。但是此时css里面的布局意图是比较难以被记录的(难以被理解就难以维护,难以重构),有其在充斥了大量Trick的情况下,这正是广大程序员/设计人员需要解决的,我们是否应该通过不断地重构来找到这个矛盾的平衡点呢?欢迎大家讨论。最后附上淘宝UED团队的小马总结的淘宝CSS编程原则:
- 尽量不使用hack
- 尽量不使用ie6不支持的选择符
能符合这两个条件的最简洁的写法,就是我们的目标。