充满 Trick 的 CSS,两难的选择

  • 田乐

2007 年 9 月 6 日

话题:Java敏捷重构最佳实践语言 & 开发架构文化 & 方法

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 编程原则:

  1. 尽量不使用 hack
  2. 尽量不使用 ie6 不支持的选择符
能符合这两个条件的最简洁的写法,就是我们的目标。
Java敏捷重构最佳实践语言 & 开发架构文化 & 方法