1. 1. 为何难以使用CSS给表单组件添加样式?
    1. 1.1. 并非所有组件受CSS的影响都是平等的
      1. 1.1.1. 还好的
      2. 1.1.2. 比较糟糕的
      3. 1.1.3. 丑陋的
  2. 2. 基本样式
    1. 2.1. 搜索框
      1. 2.1.1. 例子
    2. 2.2. 字体和文本
    3. 2.3. 盒模型
    4. 2.4. 定位
      1. 2.4.1. legend
      2. 2.4.2. textarea
  3. 3. 实例
    1. 3.1. HTML
    2. 3.2. CSS
  4. 4. 结论

本文中,我们将学习如何使用CSS来让HTML表单看起来更漂亮,这可能需要窍门才能做到。由于历史及技术上的原因,表单组件并不太适合使用CSS;而也正因为有这些困难,许多的开发者会选择创建定制表单组件来获得对外观和体验的控制。然而,在现代浏览器中,网页设计师可以拥有更多对表单元素的控制权了。让我们来深入了解下吧。

为何难以使用CSS给表单组件添加样式?

在web发展的早期,大约1995年,表单控件就已经在the HTML 2 specification中给添加到HTML了。由于表单组件的复杂性,浏览器开发商们就选择了依靠操作系统来管理和渲染它们。

几年之后,CSS诞生了,这就在技术上使得用原生组件来实现表单的做法也有了样式需求。然而在CSS的早期,给表单控件添加样式并未被优先考虑。

由于用户们习惯了在交互平台上的视觉体验,浏览器开发商不得不让表单控件可以被添加样式;而说实话,在今天也依然难以重构所有表单控件让它们可被样式化。

即使到了现在,也依然没有一个单独的浏览器实现了所有CSS 2.1规范。然而随着时间推移,浏览器开发商们已经改进了表单元素的CSS支持,虽然其可用性仍受诟病,但现在你已经可以使用CSS来给HTML表单添加样式了。

并非所有组件受CSS的影响都是平等的

如今在表单使用CSS时依然有一些困难;这些问题可归为三类:

还好的

若存在跨平台问题,一些元素可以只添加少许的样式,有如下几个结构元素:

  1. <form>
  2. <fieldset>
  3. <label>
  4. <output>

此外,还有所有的文本框组件(单行或多行),以及按钮。

比较糟糕的

一些元素只能使用很少的样式,而且得依赖一些复杂的技巧,偶尔还得用到CSS3的高级知识。

这其中包括了<legend>元素;该元素不能在跨平台时被恰当地定位。此外,复选框及单选框不能直接添加样式;然而有了CSS3之后你就可以做到这点了。placeholder的内容是不能通过标准方法来添加样式的,但所有实现了它的浏览器都会以私有的CSS伪元素或伪类的形式让你能给它添加样式。

至于要具体如何处理这些特殊情况,我们会在HTML表单高级样式一文中讨论。

丑陋的

某些元素是不能用CSS添加样式的。它们包括所有的高级UI组件比如范围滑块、颜色、日期控件,以及下拉组件(包括<select>, <option>, <optgroup>, <datalist> 等元素)。文件选择器组件也被认为是不能添加样式的,而新的<progress><meter>元素也在此之列。

这些组件的主要问题在于,它们拥有非常复杂的结构,而CSS没有足够的表现力来给这些组件的各个细节添加样式。若你非得定制这些组件,就只能依靠Javascript来构建一棵能让你添加样式的DOM树。我们将在如何创建定制表单组件一文中学习如何做到这一点。

基本样式

在用CSS给那些易于添加样式的元素的元素以样式时,你不必面对任何困难,因为它们多数表现得和其它HTML元素一样。然而,每个浏览器的用户代理样式表会导致一些不一致的情况,所以,这里会有几个技巧来帮你轻松地给它们添加样式。

搜索框

搜索框是文本框中唯一一种需要点技巧来添加样式的。在基于webkit的浏览器(chrome, safari等)中,你得用-webkit-appearance属性来作下调整。我们将会在HTML表单高级样式一文更深入地探讨该属性。

例子

1
2
3
<form>
<input type="search">
</form>
1
2
3
4
5
6
input[type=search] {
border: 1px dotted #999;
border-radius: 0;
-webkit-appearance: none;
}

在上面这张Chrome的搜索框截图中,连个文本框都设置了边框,但第一个文本框没有使用-webkit-appearance属性进行渲染,而第二个贼使用了-webkit-appearance:none。它们间的差别值得注意。

字体和文本

CSS字体和文本特性在任何组件中都可以被轻易使用(当然,你也可以在表单组件上使用@font-face)。然而,不同浏览器的行为表现通常是不一样的。某些组件默认不会从父元素那继承font-familyfont-size,同时许多浏览器会使用系统的默认样式来作为替代。要让你的表单的外观与其他内容保持一致,你可以在样式表中添加如下规则:

1
2
3
4
button, input, select, textarea {
font-family : inherit;
font-size : 100%;
}

下面的截图体现了设置之后的不同;左边是MAC OSX的Firefox中元素的默认渲染效果,其使用了系统的默认字体样式。而右边则是使用了上面的字体协调样式后的相同元素。

要使用系统默认样式还是自定义样式以适应页面内容,仍存在很多争议。这个决定权在于身为网页或web应用设计师的你身上。

盒模型

所有的文本框都完全支持CSS盒模型相关的属性(width, height, padding, margin, border)。然而以前要呈现这些组件时,浏览器都得依赖系统的默认样式。至于如何把这些样式混用到你的页面中,这得取决于你。

若你想保持这些原生组件的样子和体验,你会在给它们实现一致的样式时遇到点困难。这是因为每个组件都有它们独有的边框、内边距和外边距的规定。所以,如果你希望在几个不同的组件间保持相同的大小,你就得使用box-sizing属性:

1
2
3
4
5
6
7
8
input, textarea, select, button {
width : 150px;
margin: 0;
-webkit-box-sizing: border-box; /* 兼容基于Webkit的旧版浏览器 */
-moz-box-sizing: border-box; /* 兼容基于Gecko的旧版浏览器(Firefox < 29) */
box-sizing: border-box;
}

上面的截图中,左边一列是不使用box-sizing构建的,而右边一列则使用了该属性并赋予其值border-box。可见设置该属性让所有的元素都占据了相同的空间大小,而覆盖了系统给各种组件的默认规则。

定位

定位HTML表单元素通常不是什么大问题,然而有两个特殊元素值得你关注一下:

legend

<legend>元素可以很好地支持样式,除了定位。在每种浏览器中,<legend>元素都位于其父<fieldset>元素的上边框以上,根本没办法在HTML文档流中改变其定位、让其远离那个上边框。你只能使用position属性来让其绝对或相对定位,否则它就只能视作是fieldset边框的一部分。

以为无障碍技术的原因,使得<legend>成为很重要的元素(它作为fieldset中各个表单组件的label,并以此被无障碍设备读出),通常他会和一个标题做搭配,并以无障碍技术可识别的形式隐藏起来,就像这样:

1
2
3
4
<fieldset>
<legend>Hi!</legend>
<h1>Hello</h1>
</fieldset>
1
2
3
4
5
legend {
width: 1px;
height: 1px;
overflow: hidden;
}

textarea

所有浏览器都默认将textarea元素当作内联元素,并让它与文本的底线对齐。而这种设定通常并不是我们想要的,使用display属性可以很容易就将其从inline-block改为block。但若你还想把它当内联元素使用,那通常得改变其垂直对齐方式:

1
2
3
textarea {
vertical-align: top;
}

实例

来看一个给表单以样式的例子吧,通过例子,许多相关的知识点会更容易理解些。而我们要构建的,是如下图所示的contact表单:

HTML

相比本指南第一篇文章,这里的HTML稍微多了点内容;只有几个额外的字段和一个标题而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<form>
<h1>to: Mozilla</h1>
<div id="from">
<label for="name">from:</label>
<input type="text" id="name" name="user_name">
</div>
<div id="reply">
<label for="mail">reply:</label>
<input type="email" id="mail" name="user_email">
</div>
<div id="message">
<label for="msg">Your message:</label>
<textarea id="msg" name="user_message"></textarea>
</div>
<div class="button">
<button type="submit">Send your message</button>
</div>
</form>

CSS

有趣的部分开始了,但在我们编码之前,还需要三个额外的资源:

  1. 明信片背景
  2. 一套打字机字体:fontsquirrel.com上的”Secret Typewriter”
  3. 一套手写字体:fontsquirrel.com上的”Journal”

现在我们可以投入写代码了。首先,我们要准备好@font-face的定义以及<body><form>元素的基本样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@font-face{
font-family : "handwriting";
src : url('journal.eot');
src : url('journal.eot?') format('eot'),
url('journal.woff') format('woff'),
url('journal.ttf') format('truetype');
}
@font-face{
font-family : "typewriter";
src : url('veteran_typewriter.eot');
src : url('veteran_typewriter.eot?') format('eot'),
url('veteran_typewriter.woff') format('woff'),
url('veteran_typewriter.ttf') format('truetype');
}
body {
font : 21px sans-serif;
padding : 2em;
margin : 0;
background : #222;
}
form {
position: relative;
width : 740px;
height : 498px;
margin : 0 auto;
background: #FFF url(background.jpg);
}

然后我们来定位标题和所有表单元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
h1 {
position : absolute;
left : 415px;
top : 185px;
font : 1em "typewriter", sans-serif;
}
#from {
position: absolute;
left : 398px;
top : 235px;
}
#reply {
position: absolute;
left : 390px;
top : 285px;
}
#message {
position: absolute;
left : 20px;
top : 70px;
}

接下来,我们得开始对表单元素自身做配置了。首先,确保<label>使用了正确的字体:

1
2
3
label {
font : .8em "typewriter", sans-serif;
}

文本框需要使用一些公共样式。简单起见,可以移除它们的边框和背景,然后重新定义其内外边距:

1
2
3
4
5
6
7
8
9
10
input, textarea {
font : .9em/1.5em "handwriting", sans-serif;
border : none;
padding : 0 10px;
margin : 0;
width : 240px;
background: none;
}

而当这些输入框获得焦点时,还得让它们用一个浅灰色、半透明背景做高亮。注意为了移除一些浏览器自带默认的高亮,还需要配置outline属性:

1
2
3
4
5
input:focus, textarea:focus {
background : rgba(0,0,0,.1);
border-radius: 5px;
outline : none;
}

现在我们的文本框已经整好了,但我们还得调整单行和多行文本框以作适配,因为通常它们看起来是一点都不相同的。

对单行文本框需要一些微调以让其在IE下看起来漂亮点。IE不是基于字体的自然高度来定义文本框高度的(但其它所有浏览器都这么做),要修复这点,我们得给文本框指定一个明确的高度,如下所示:

1
2
3
4
input {
height: 2.5em; /* 针对IE */
vertical-align: middle; /* 可选配置,能在旧版IE中看起来漂亮点 */
}

<textarea>元素应被预设置为块级元素进行渲染。这里还有两个重要的属性,resizeoverflow。由于我们采用固定大小的设计,所以得使用resize属性来防止用户改变多行文本框的大小。而overflow属性则让文本框在不同浏览器下的效果趋于一致;因为有的浏览器默认使用值auto而另一些使用值scroll。本例中,最好得保证各个浏览器下都使用auto

1
2
3
4
5
6
7
8
9
10
11
textarea {
display : block;
padding : 10px;
margin : 10px 0 0 -10px;
width : 340px;
height : 360px;
resize : none;
overflow: auto;
}

<button>元素可以很方便地使用CSS;这样你就可以尽情发挥了,即使用上伪元素也没问题!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
button {
position : absolute;
left : 440px;
top : 360px;
padding : 5px;
font : bold .6em sans-serif;
border : 2px solid #333;
border-radius: 5px;
background : none;
cursor : pointer;
-webkit-transform: rotate(-1.5deg);
-moz-transform: rotate(-1.5deg);
-ms-transform: rotate(-1.5deg);
-o-transform: rotate(-1.5deg);
transform: rotate(-1.5deg);
}
button:after {
content: " >>>";
}
button:hover,
button:focus {
outline : none;
background: #000;
color : #FFF;
}

随意尝试下吧,试了你才知道你可以做到!

结论

如你所见,如果我们想构建只含文本框和按钮的表单,那么用CSS来提供样式是件很容易的事。若你还想了解多些能让你更轻松地处理表单组件的CSS技巧,可以参见normalize.css项目的表单部分。

下篇文章,我们会学习如何处理那些属于“比较糟糕的”和“丑陋的”类别的表单组件。