# 模块规则

前文简短提及过模块是页面中更加离散的组件,例如,导航栏,插图,对话框,小部件等等。模块是页面的血肉,它位于布局组件中,有时也会位于其他模块。每个模块都被设计成独立的组件,这样做,页面将会更加灵活。如果设计得当,模块可以轻易地应用于布局的各个部分。

当为模块定义一系列规则时,应该避免使用ID和元素选择器,一定要使用类名。一个模块可能包含多个元素,最好使用后代或者孩子选择器去选择这些元素。

/* 模块例子 */

.module > h2 {
    padding: 5px;
}

.module span {
    padding: 5px;
}

# 避免使用元素选择器

如果使用元素选择器时,我们知道元素是位于怎样的位置,那我们可以使用孩子或者后代选择器配合元素选择器。例如,如果我们知道span元素在模块当中不管怎么样都是相同的样式,那使用.module span是极好的。

/* 编写通用元素样式 */
<div class="fld">
    <span>Folder Name</span>
</div>

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

但是问题是随着项目变得越来越复杂,你越需要对组件的功能进行扩展,那就会受通用元素规则的限制了。

<!-- 编写通用样式 -->
<div class="fld">
    <span>Folder Name</span> 
    <span>(32 items)</span>
</div>

如上述例子。我们不想图标出现在我们文件夹模块的两个span元素上,按前文的写法,肯定不顶用。

怎么做呢?

只要使得选择器具有语义即可spandiv选择器没有任何语义,head选择器可能有一些,但定义在元素上的类名却可以有很多语义。

<!-- 编写通用样式 -->
<div class="fld">
    <span class="fld-name">Folder Name</span> 
    <span class="fld-items">(32 items)</span>
</div>

为这些元素添加类选择器后,元素的语义化程度确实大大提高,编写样式时可能产生的歧义也没了。

如果你还是想使用元素选择器,请确保处于类选择器的下一层。换句话说,你只能在使用孩子选择器情况下使用元素选择器。你得十分确信元素不会被其他元素所影响,没有任何歧义。因为HTML元素(像span或者div)的语义越通用,就越可能产生冲突。而像一些语义更加明确的元素(像head)可以更多地使用,因为这样元素选择器出现歧义的概率会比较低。

# 新的上下文

使用模块还可以让我们更加了解上下文可能在哪里发生变化。例如,新的上下文可能会发生在布局级别或者模块的根部。

# 子类化模块

当我们在不同的部分都有相同的模块时,我们的第一直觉可能是使用父选择器对不同的模块编写样式

/* 子类化 */
.pod { 
    width: 100%; 
}
.pod input[type=text] { 
    width: 50%; 
}
#sidebar .pod input[type=text] { 
    width: 100%; 
}

这种方法的问题在于一些特殊的问题会迫使你不得不添加更多的选择器,或者去使用!important

我们接着展开来讲我们的示例.pod.pod中的input有两个不同的宽度。整个网站,input旁边总是有一个label,所以input区域一般是宽度的一半。但在sidebar当中,宽度太小了,所以我们将input增加到100%并把label放在其顶部。现在,我们需要往我们的页面中添加一个新的组件。这个组件和.pod使用相同的样式,所以我们复用这个类。但是,这个pod比较特殊,在网站的任何地方都需要180px的宽度。

/* 对抗模块的特殊性 */

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

.pod-callout { 
    width: 200px; 
}
#sidebar .pod-callout input[type=text],
.pod-callout input[type=text] { 
    width: 180px; 
}

我们可以看到,增加了两倍的选择器数量只是为了覆盖#sidebar指定的样式。

其实我们需要做的就是将sidebar中的pod看做pod的一个子类,明白这一点样式就十分简单了。

/*  对抗模块的特殊性 */
.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; 
}

通过对模块进行子类化,基础模块和子模块的类名都是能应用于HTML元素。

<!-- HTML中的子模块类名 -->
<div class="pod pod-constrained">...</div>
<div class="pod pod-callout">...</div>

不要基于某个位置设置特殊的样式。如果你想改变一个模块的外观,并能在任何地方都能使用,为该模块写一个子类即可。

而为了对抗特殊性,如果不考虑IE6,你可以多写一个类名,就如下面的例子

/* 子类化 */

.pod.pod-callout { }

<!-- In the HTML -->
<div class="pod pod-callout"> ... </div>

你可能会考虑由于加载顺序导致的特殊性问题。例如,在雅虎邮箱,可能有来自不同地方的代码。我们有一个基础的按钮样式,还有一系列的特殊按钮。但是,当我们点击将一个联系人添加到你的地址簿时,这时我们从另一个不同的产品(地址簿)中加载了一个特殊的组件。地址簿加载了自己的基础按钮样式,因此覆盖了我们所有的子类按钮样式。

如果加载顺序在你的项目中是一个影响因素,一定要注意特殊性问题。

虽然可以使用ID选择器为更具体的布局组件提供特殊样式,但是子类化的模块却可以更容易在网站的不同部分复用,而且可以避免由于特殊性导致的不必要的问题。