HTML和CSS的分离
特别声明:此篇文章由Jekst根据Jonathan Snook的英文文章原名《Decoupling HTML From CSS》进行翻译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://coding.smashingmagazine.com/2012/04/20/decoupling-html-from-css以及作者相关信息
——作者:Jonathan Snook
——译者:Jekst
多年以来,web标准社区一直在讨论关注点——分离。把CSS样式规则从javascript代码和HTML结构里分离出来。我们确实是这么做的,不是吗?CSS规则放到了独立的文件里,javascript代码放到了另一个独立的文件里,HTML文件就只剩下HTML结构了,这样做既简洁又美观。
CSS Zen Garden向我们证明了我们只需要改变CSS样式文件就能将一个设计转变成多种样式的设计。但是,我们很少注意到这样做的另一面——一个工程里出现这种情况的可能性很大:HTML文档结构的改变。当我们修改了HTML文件后,我们不得不回过头来从新修改CSS文件来适应新的HTML文件。
由此看来,我们并没有真正的将HTML和CSS分开,不是吗?我们不得不在HTML和CSS两个文件里都做修改。
探索方法
在我的职业生涯中,我很高兴能够参与数百个不同的网站和web应用程序的开发。在绝大多数的项目中,我是唯一一个编写HTML和CSS的开发者。我开发了一种开发网站的方法,对我来说,这种方法很好用。
最近两年的时间,我从事于雅虎,参与了 Mail、Messenger、Calendar及其他一些项目的开发。和一个较大的团队一起开发一个较大的项目是一次很棒的经历。一个小团队设计员与一个大团队样式设计师们合作, 为多个团队的工程师建立所有的HTML和CSS。
从多个方面来看,这是我参与过的规模最大的工程:
- 雅虎拥有大量的用户,仅邮箱用户就达到3亿左右。
- 遍布多个团队的数百人从事HTML和CSS开发。
- 我们开发的组件系统在多个工程间使用
就是在雅虎工作期间,我开始真正的考虑我和团队该怎样去构建网站。我们遇到过哪些难题,又能如何避免他们?
我看过其他人的做法, Nicole Sullivan的Object-Oriented CSS,Jina Bolton的“CSS Workflow” ,和Natalie Downe的“Practical, Maintainable CSS”,还有别人的一些文章,这里只列出了一部分。
根据我的想法,我写了一本关于长表单样式的指南,叫做“Scalable and Modular Architecture for CSS”名字有点长,大家可以简称为“SMACSS”(发音“smacks”)。在我提炼并扩张开发CSS渠道的同时,这份指南也会随之继续扩展用于css开发。
结果我发现设计师(包括我)写得CSS规则和被应用样式的HTML有很大关联。为了更少重构、更灵活开发,我们怎么将HTML和CSS分离呢?
换句话说,在任何元素上,我们怎样避免使用!important,或者避免掉进选择器的陷阱呢?
复用样式
以前,我们给需要加样式的HTML元素使用font标签、应用background属性(指attributes,不是CSS里的background属性,是HTML 标签里的background)。当然,这种方式非常不现实, 因此就出现了CSS。CSS能够使我们从页面的一个部分复用另一部分的样式,实现样式的复用。
比如,一个导航菜单,有一些样式一样的菜单项,在每个项使用行样式是不现实的。因此,我们开始看到的CSS是这样的:
#nav { margin: 0; padding: 0; list-style: none; } #nav li { float: left; } #nav li a { display: block; padding: 5px 10px; background-color: blue; }
无疑每个菜单项添加了float:left属性,链接加上了display:block; padding:5px 10px;样式。很高效,我们要的效果达到了。看到这些CSS规则,大家就可以看到预期的HTML结构:
<ul id="nav"> <li><a href="/">Home</a></li> <li><a href="/products">Products</a></li> <li><a href="/contact">Contact Us</a></li> </ul>
现在,客户反馈说:“当点击‘Products‘菜单项的时候,我想要一个下拉的子菜单。并且要在每个页面加上这样的效果!”因此,我们的HTML结构变了:
<ul id="nav"> <li><a href="/">Home</a></li> <li><a href="/products">Products</a> <ul> <li><a href="/products/shoes">Shoes</a></li> <li><a href="/products/jackets">Jackets</a></li> </ul> </li> <li><a href="/contact">Contact Us</a></li> </ul>
现在,Products有了一个子菜单,子菜单里也有连接。我们的菜单是一个水平的导航,但是客户还要一个垂直的下拉菜单,所以我们给子菜单添加一些CSS规则以满足客户的需求。
#nav ul { margin: 0; padding:0; list-style:none; } #nav li li { float: none; } #nav li li a { padding: 2px; background-color: red; }
从某种程度上说,问题解决了。
减少适用性深度
可能使用CSS最常见的问题就是管理特定的样式。页面上会有多个CSS样式规则给一个特定的元素添加样式。像我们的菜单,初始的规则会给导航和下拉菜单里的菜单项和链接都加上样式。这样做并不好。
通过添加额外的元素选择器,我们可以添加特定的样式,使我们的菜单样式不再继承导航的样式。
但是,随着项目复杂程度的增加,这会变得跟猫捉老鼠的游戏一样,不知道哪个元素用的哪个样式。因此我们应该限制CSS规则的影响范围。导航样式应该只应用到属于导航的元素,对这些导航元素产生效果;菜单样式应该应用到属于菜单的元素,对这些菜单元素产生效果。
我在SMACSS中提到过这个影响 - “适应性深度”。 意思是一组特别的规则影响周围元素的深度。例如,当HTML结构包括我们的菜单时,一个像#nav li a这样的样式规则有5个层次的深度:从最外层的ul 到li,到里面的ul,li,a。
适用性层次越深,HTML的元素受到这种样式的影响越大,HTML和CSS的耦合程度越高。
大多数管理CSS的目标——特别是在较大的工程里,是限制适用性的深度。换句话说,写的CSS样式规则只对我们要应用的元素产生效果。
子选择器
一个限制CSS规则作用范围的工具就是子选择器(>)。如果不关心IE6(还好,我们大多数都不关心IE6),子选择器应该是CSS中一个常规的部分。
子选择器限制了选择器的范围。回过头看看我们的导航例子,我们使用子选择器来限制导航样式的范围,这样才不会影响菜单。
#nav { margin: 0; padding: 0; list-style: none; } #nav > li { float: left; } #nav > li > a { display: block; padding: 5px 10px; background-color: blue; }
对于子菜单,我们添加一个类选择器。这样使子菜单更具有描述性,并且为菜单的其他部分提供了一个基本的样式。
.menu { margin: 0; padding: 0; list-style: none } .menu > li > a { display: block; padding: 2px; background-color: red; }
我们所做的就是限制CSS规则的作用范围,将两个不同的视觉效果分成两个单独的CSS块:一个用于导航列表,一个用于子菜单。我们已经向模块化代码和解耦HTML和CSS迈了一小步。
分类代码
限制适用性的深度有助于最小化一个样式对HTML文档中层次较深的元素的影响。但是,还有一个问题就是,我们在CSS中使用了元素选择器,这样做的结果是依赖的HTML结构将永远不能改变,一旦改变,这些样式规则就会不起作用。在导航和菜单这个例子中,总是一个有很多列表项(li)的列表(ul),每个列表项(li)都包含一个链接。这些模块缺乏灵活性。
我们看一个在很多设计中经常见到的例子:一个box,包含一个标题块和一内容区域。
<div class="box"> <h2>Sites I Like</h2> <ul> <li><a href="http://smashingmagazine.com/">Smashing Magazine</a></li> <li><a href="http://smacss.com/">SMACSS</a></li> </ul> </div>
我们给这个例子加点样式。
.box { border: 1px solid #333; } .box h2 { margin: 0; padding: 5px 10px; border-bottom: 1px solid #333; background-color: #CCC; } .box ul { margin: 10px; }
客户反馈说:“这个box很好,能不能再添加另一个有关这个网站的box。
<div class="box"> <h2>About the Site</h2> <p>This is my blog where I talk about only the bestest things in the whole wide world.</p> </div>
在前一个例子中,为了和上面标题对齐,列表设置了magin属性。对这个新的例子,我们也需要添加类似的样式。
.box ul, .box p { margin: 10px; }
如果box里的内容部分(这里指的是内容标签,如p,ul等,不是里面的内容)不再变化,这样做完全可以。但是,这个练习的关键点是要意识到网站的结构和内容是会变的。因此,我们不得不看的远点,并且要意识到有很多我们可能会使用的替代的元素。
为了让它灵活点,我们使用class定义这个模块的不同部分。
.box .hd { } /* this is our box heading */ .box .bd { } /* this is our box body */
当应用到HTML上是,就像这样:
<div class="box"> <h2 class="hd">About the Site</h2> <p class="bd">This is my blog where I talk about only the bestest things in the whole wide world.</p> </div>
说明意图
页面的不同元素可能包含一个标题部分和一个内容部分。由于他们是box的子选择器元素,他们的样式不受其他样式的影响。但是当我们看HTML时,这些类的意图并不是很明显。所以我们应该表明在box模块中添加的hd 和bd特殊类的意图。
.box .box-hd {} .box .box-bd {}
有了这个改进的命名约定,在命名CSS选择器时就不需要组合选择器了。最后的CSS是这样的:
.box { border: 1px solid #333; } .box-hd { margin: 0; padding: 5px 10px; border-bottom: 1px solid #333; background-color: #CCC; } .box-bd { margin: 10px; }
这样做的好处就是每个规则只对那个应用了该规则的元素起作用,CSS也变得容易阅读,容易调试了。什么元素应用什么样式规则,什么样式在起作用,都清清楚楚的。
还没有结束
我么仅看了两种分离HTML和CSS的方法
- 使用子选择器
- 使用类选择器
除此之外,我们还看到了命名约定,使得代码更清晰、快速、简洁、更容易理解。
还有一些概念,我在Scalable and Modular Architecture for CSS这篇文章中提到了,希望大家去读一读,了解更多。
附言
除了上面的连接资源外,大家不妨去看看BEM的相关资料,它是一个建立维护CSS的另一个框架。Mark Otto已经编写了Twitter Bootstrap的开发文档,包括最近发表的跟限制样式作用范围相似的文章 “Stop the Cascade”。
译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!
关于Jekst
常用昵称jekst,目前就职于北京一家信息技术公司,主要从事。net系列的开发,热爱前端,对css、jQuery有浓厚兴趣,喜欢参加技术交流活动。欢迎交流共勉:新浪微博。
如需转载烦请注明出处:
英文原文:http://coding.smashingmagazine.com/2012/04/20/decoupling-html-from-css
中文译文:http://www.w3cplus.com/css/decoupling-html-from-css.html