如何组织Html元素与如何进行CSS命名(上)

最近下决心整理一份对页面元素的组织规则和CSS的命名规则,因为深深感受到如果页面上元素太多,没有规则的命名和组织会让网页的维护性大打折扣。参考了几篇文章并且发散了一下,在这里和大家分享

 

BEM(Block Element Modifier)

本文同时也发表在另一个独立博客 http://qingbob.com/blog/如何组织Html元素与如何进行CSS命名(上)

如何组织页面上的元素,或者说安排元素之间的关系势必会对css命名产生影响;css命名也是对元素关系的映射。BEM这个方法把元素分为三类,代指 Block , Element , Modifier 。

举个例子,通常我们会把页面分为header, body, footer部分,可能header部分里面又包括了logo, search, login模块。我们可以把Block理解为一个已经封装好了的组件,比如一个搜索模块(这里说的模块统统指一系列html元素,而非逻辑上的功能代码),它是相对于同级元素比较独立的(也相对于element元素)。

Element则是block中实现具体功能的部件,比如在搜索模块中最起码需要一个按钮(button),一个输入框(input),它和block的重要区别之一是,它没有block那么独立,一旦离开了上下文环境(比如它所在的block),它失去功能上的意义了。

这就非常灵活了,因为某一个block可以是它父元素或者其他元素的element,比如我们单独看搜索模块可能是一个独立的block,但是搜索模块通常又是放在页面的header中,那么搜索模块此时又成了header的element,而header又是一个更大的block。

接下来,BEM作者给出了CSS的具体命名规则希望是

  • 一个block必须有唯一的名字(class),比如
<u lclass="menu">
  …
</ul>

 

  • 而element的命名需要包括它所属的block的名字,并且以分隔符分隔开:
<ul class="menu">
    <li class="menu__item">…</li>
    <li class="menu__item">…</li>
</ul>

 

当然modifier就更好理解了,比如在一个tab模块中,我们需要突出某个tab,就需要给它添加一个的modifier作用的class,这里的modifier可以使特殊的状态,也可以是特殊的属性。就拿上面那个menu例子来说,我们想增大字体,想标识当前选中的菜单项,就可以添加

<ul class="menu menu_size_big">
    <li class="menu__item">Index</li>
    <li class="menu__item    menu__item_state_current">Product</li>
<li class="menu__item">Contact</li> </ul>

 

巧合的是,在写这篇文章的同时Smashing Magazine上同时发布了一篇谈BEM现在与将来的文章The Evolution Of The BEM Methodology。主要对BEM过去的里程碑,每个里程碑所得出的一些方法论做了一些总结。比如说谈到BEM的起源其实是为了解决实际项目中css选择器冗长的问题,比如

.result .albums .album .buy {
        float: left;
     padding:0.4em1em01.6em;
}

.result .albums .info i {
     font-size:85%;
}

 

有甚者

.b-foot div div div div div
{
 background-position:71%;
 background-image: url(../i/foot-5.png);
}

.b-foot div div div div div div { background-position:87%; background-image: url(../i/foot-6.png);
}

 

虽然现在看起来很可笑,但我觉得这却是实际中遇见的问题,必须承认我自己有时也陷入这样的怪圈

这篇文章谈为什么有BEM的来龙去脉更生动一些。有兴趣的同学可以看看。其实BEM是一系列的方法论,甚至还包括文件的命名的文件夹分类规则,XSL templates,甚至整个可供参考的框架。

因为在这里我只是作为一个组织html元素和css命名的其中一个方法,只做了简明的介绍和总结。

 

SMACSS(Scalable and Modular Architecture for CSS)

 

这个方法论将css分为5类,分别是

  • Base
  • Layout
  • Module
  • State
  • Theme(忽略这个先)

下面一一进行介绍

Base:

基本(base)规则即那些只使用元素选择器,后代选择等(从不涉及class或者id)的规则,比如

body, form {
    margin:0;
    padding:0;
}

a:hover {
    color:#03F;    
}

 

你可以理解为定义一些全局的css样式。通常这种工作也可以交给reset.css或者normalize.css来完成

 

Layout Rules:

这里的布局(Layout)指页面上比较大块的区域,比如header,body, footer。而这里的layout rule也分为两类,一类是通过id定义的,比如

#header, #article, #footer {
    width:960px;
    margin:auto;
}

#article { border: solid #CCC; border-width:1px00;
}

 

还有一类可能是在你用了一些css框架的情况下,比如960.gs

.container_12 .grid_6,.container_16 .grid_8 {
  width:460px;
}

作者建议与layout有关的css规则以l-开头,比如

.l-fixed#sidebar {
    width:200px;
}
Module Rules:

比如说一些登陆,搜索,文章,这样的元素组合我们就可以称之为module。对于这样一些元素的css命名,作者说就免了前缀,直接用模块名称好了,比如

.login {
    width:200px;
}

作者在这里强调的是,避免使用元素选择器。比如开始我们有这么一段html, 有这么一段样式

<div class="fld"><span>Folder Name</span></div>
/* The Folder Module */.fld > span {
    padding-left:20px;
    background: url(icon.png);
}

 

问题是当我们的项目变得庞大,需要增加一个或者更多span标签时

<div class="fld"><span>Folder Name</span><span>(32 items)</span></div>

这会就傻×了吧。所以最好是给标签添加上有语义的class名称,比如

<div class="fld"><span class="fld-name">Folder Name</span><span class="fld-items">(32 items)</span></div>

还有一种情况,当我们在不同的section中使用了同一个module时,我们可能需要根据module所在的section来重新定义样式,比如

.pod { 
    width:100%;
}
.pod input[type=text]{ 
    width:50%;
}
#sidebar .pod input[type=text] { 
    width:100%;
}

 

但这样还是会产生问题,会让css变得没有规则和难以维护,所以作者建议添加一个子模块css(Subclassing Modules),比如这么做

<div class="pod pod-constrained">...</div><div class="pod pod-callout">...</div>
.pod { 
    width:100%;
}
.pod input[type=text]{ 
    width:50%;
}
.pod-constrained input[type=text]{ 
    width:100%;
}
.pod-callout { 
    width:200px;
}
.pod-callout input[type=text]{ 
    width:180px;
}
State Rules

state 与之前的modifier概念类似,这种类型的class只起一些修饰作用。并且作者建议使用is-开头,比如

<div id="header"class="is-collapsed">
<form>
<div class="msg is-error"> There is an error! </div>
<label for="searchbox" class="is-hidden">Search</label>
<input type="search"id="searchbox">
</form>
</div>

 

要注意它和之前sub-module的区别

  • state规则给layout或者module用都行
  • state规则通常由javascript有关(比如错误,高亮,是否折叠),而sub-module是静态不能随意修改的。只是为了区分模块之间的区别

在这个方法论的文章中,我觉得最有价值的一篇是谈到css的可访问性的。首先作者给出的两条建议是

  • css不应该依赖DOM树的结构
  • css选择器不宜太深

假如我们有这么一段css

#sidebar div {
    border:1px solid #333;
}
#sidebar div h3 { margin-top:5px;
}

#sidebar div ul { margin-bottom:5px;
}

 

这段css中的div可以看做一个组件,是由h3和ul组成的。如果我们想把这个组件又放在footer中怎么办,看下面这段代码怎么样

#sidebar div, #footer div {
    border:1px solid #333;
}

#sidebar div h3, #footer div h3 { margin-top:5px;
}

#sidebar div ul, #footer div ul { margin-bottom:5px;
}

 

代码这么冗余的原因就是因为它与dom联系的太紧密,不如把这层依赖关系去掉,给它加上一个独立的class

.pod {
    border:1px solid #333;
}
.pod > h3 { 
    margin-top:5px;
}
.pod > ul {
    margin-bottom:5px;
}

 

我们试图在可维护性,性能,和可读性三者之间保持平衡,虽然css选择器具有一定深度意味着更少的class,但是它增加了可维护性和可读性。除非你压根就不想使用class。

下一期最后看看高手Nicolas的方法论。并且结合淘宝和人人的css规则看看实际的一些应用情况。

posted @ 2013-03-14 00:03  hh54188  阅读(2467)  评论(5编辑  收藏  举报