【译】HTML表单高级样式
阅读原文时间:2023年07月08日阅读:1

在本文中,我们将了解如何在HTML表单上使用CSS,为那些难于自定义的表单组件加以样式。如前文所述,文本框和按钮很适合使用CSS,而现在我们得来探索HTML表单样式的那些坑了。

在进一步讨论前,先回顾下两种HTML表单组件:

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

丑陋的
别指望用CSS给这些元素添加样式了。在最好的情况,你还能写一点不能跨浏览器支持的代码,而且还不可能完全控制这些元素的样式。

除了文本框和按钮,表单组件面临的最大问题,是CSS在多数情况下没有足够的表现力来恰当地给复杂组件添加样式。

近来HTML和CSS的演进已在拓展CSS的表现力:

虽然上述这些都是个好的开始,但其中仍有两个问题:第一,某些浏览器并不会实现CSS2.1之外的特性。第二,这些改进并未好到能处理诸如日期选择器之类的复杂组件。

浏览器厂商也为拓展CSS在表单上的表现力做了些试验,最好得了解下哪些可以使用。

警告:虽然这些实验挺有趣的,但它们并非标准、并不可靠。若你要使用它们(通常你也不会这么做),你得自己担起风险,而且使用非标准属性也是可能阻碍Web发展的做法

控制表单元素的外观

基于 WebKit- (Chrome, Safari) 和 Gecko- (Firefox) 的浏览器为HTML组件提供了最高等级的定制。这些定制也是跨平台的,所以浏览器需要一个机制来转换那些能被改变样式的表单组件的原生外观和体验。

于是它们使用了私有属性:-webkit-appearance-moz-appearance这些属性是非标准的,也不应被使用。实际上,它们在Webkit和Gecko上的表现也不尽相同。但是,有一个值是应该知道的:none,使用该值你就可以获得对组件样式的(几乎所有)控制权。

所以,若你在一个元素上应用样式时遇到问题,可以试着使用这些私有属性(译注:用none值覆盖默认值)。下面我们会看到几个例子,但最为人熟知的用例是重置Webkit浏览器上搜索框的样式。

<form>
    <input type="search">
</form>

<style>
input[type=search] {
    border: 1px dotted #999;
    border-radius: 0;

    -webkit-appearance: none;
}
</style>

注意:在我们讨论Web技术时,是难于预测未来的,但拓展CSS的表现力确实很难。另一些做了探索工作的标准如Shadow DOM提供了新的视角。我们对完全可配置样式的表单的追求还远未结束。

多选框和单选框

给多选框和单选框添加样式是很让人凌乱的。例如,多选框和单选框的大小往往不会发生改变,而且不同浏览器的表现相当不同。

一个简单例子

考虑如下示例:

<span><input type="checkbox"></span>

span {
    display: inline-block;
    background: red;
}

input[type=checkbox] {
    width : 100px;
    height: 100px;
}

不同浏览器的处理如下:

浏览器

渲染效果

Firefox 16 (Mac OSX)

Chrome 22 (Mac OSX)

Opera 12.01 (Mac OSX)

Internet Explorer 9 (Windows 7)

Internet Explorer 7 (Windows XP)

复杂点的例子

由于Opera和IE没有诸如-webkit-appearance-moz-appearance之类的特性,所以使用这类特性是不太合适的。幸运的是,在这种情况下用CSS还能找出解决办法来。举一个常见的例子:

<form>
  <fieldset>
    <p>
      <input type="checkbox" id="first" name="fruit-1" value="cherry">
      <label for="first">I like cherry</label>
    </p>
    <p>
      <input type="checkbox" id="second" name="fruit-2" value="banana" disabled>
      <label for="second">I can't like banana</label>
    </p>
    <p>
      <input type="checkbox" id="third" name="fruit-3" value="strawberry">
      <label for="third">I like strawberry</label>
    </p>
  </fieldset>
</form>

加一些基本样式:

body {
  font: 1em sans-serif;
}

form {
  display: inline-block;

  padding: 0;
  margin : 0;
}

fieldset {
  border : 1px solid #CCC;
  border-radius: 5px;
  margin : 0;
  padding: 1em;
}

label {
  cursor : pointer;
}

p {
  margin : 0;
}

p+p {
  margin : .5em 0 0;
}

现在,我们来加样式以获得一个定制的复选框。

我们的计划是用我们自己的图像来替换原生的复选框。首先得准备一张具有所有复选框所需状态的图像,这些状态有:未勾选、已勾选、禁用未勾选、禁用已勾选。该图像可用CSS雪碧图来做:

先从隐藏原生的复选框开始,我们只是简单地把它们从页面的可视范围中挪出。这里有两个要重点考虑的事:

  • 别使用display:none来隐藏复选框,因为如前面提到的,我们需要保证复选框对用户可用。使用display:none的话,复选框就不再是用户可访问的,即不能再勾选或者不勾选它。

  • 我们将使用一些CSS3选择器来实现我们的样式。为支持老旧浏览器,可以在我们要用的选择器前加:root伪类。在已有的实现中,支持我们需要的选择器的浏览器也支持:root伪类,而剩下的浏览器就不支持了。所以这是一种用来识别老旧浏览器的方便做法,老旧浏览器中将会看到普通的复选框、而现代浏览器中将会看到定制的复选框。

    :root input[type=checkbox] {
    /* 原生的复选框会从页面的可视范围中被挪出 */
    position: absolute;
    left: -1000em;
    }

现在我们已经移除了原生的复选框,可以添加我们自己的了,这里会在原生复选框后面的<label>元素使用:before伪元素。下面的选择器中,我们先用属性选择器来获取复选框;然后使用相邻兄弟选择器来获取原来复选框后的label。最后我们通过给:before伪元素添加样式,用其来显示我们定制的复选框。

:root input[type=checkbox] + label:before {
  content: "";
  display: inline-block;
  width  : 16px;
  height : 16px;
  margin : 0 .5em 0 0;
  background: url("https://developer.mozilla.org/files/4173/checkbox-sprite.png") no-repeat 0 0;

/* 下一属性用于在文本基线调整复选框的位置 */

  vertical-align: bottom;
  position: relative;
  bottom: 2px;
}

接下来用原来复选框的:checked:disabled伪类来改变我们定制的复选框的状态。由于我们使用了CSS雪碧图,我们只需要调整背景的位置而已。

:root input[type=checkbox]:checked + label:before {
  background-position: 0 -16px;
}

:root input[type=checkbox]:disabled + label:before {
  background-position: 0 -32px;
}

:root input[type=checkbox]:checked:disabled + label:before {
  background-position: 0 -48px;
}

最后也是很重要的一步:当用户使用键盘在不同表单组件间浏览时,每个组件应该能看到聚焦的效果。由于我们隐藏了原生的复选框,所以只能自己实现这一特性来让用户知晓他们正处于何处。下列的CSS实现了对我们的定制复选框的聚焦:

:root input[type=checkbox]:focus + label:before {
  outline: 1px dotted black;
}

最终效果如下:

演示

处理选择框噩梦

<select>元素被认为是一个“丑陋的”组件,因为不太可能给它添加跨平台的样式。当然,还是有一些可以探讨的东西的,这里就不长篇大论了,先看个例子:

<select>
  <option>Cherry</option>
  <option>Banana</option>
  <option>Strawberry</option>
</select>

select {
  width   : 80px;
  padding : 10px;
}

option {
  padding : 5px;
  color   : red;
}

后面的表格展示了不同浏览器如何在两种情况下处理这一样式。(渲染效果中)前两列只是简单的例子,后两列则使用了些定制的CSS来获得对组件外观的更多控制,如下所示:

select, option {
  -webkit-appearance : none; /* 获得对Webkit浏览器里外观的控制 */
  -moz-appearance : none; /* 获得对Gecko浏览器里外观的控制 */

  /* 获得对Presto (Opera) 和 Trident (IE)浏览器里外观的控制
     注意这也能在Gecko浏览器里起作用,且对Webkit浏览器有副作用 */
  background : none;
}

浏览器

普通渲染(关闭)

普通渲染(打开)

调整后渲染(关闭)

调整后渲染(打开)

Firefox 16 (Mac OSX)

Firefox 16 (Windows 7)

Chrome 22 (Mac OSX)

Chrome 22 (Windows 7)

Opera 12.01 (Mac OSX)

Internet Explorer 9 (Windows 7)

N/A

N/A

Internet Explorer 7 (Windows XP)

N/A

N/A

如你所见,即使使用了 -*-qppearance 属性,仍会有问题存在:

  • padding属性在不同操作系统和浏览器中的处理是不一致的。

  • 老旧的IE不支持平滑的样式。

  • 火狐没有能给予下拉箭头样式的方式。

  • 若想要给下拉菜单中的<option>元素以样式,则Chrome和Opera在不同系统下的表现不尽相同。

同时,在本例中,我们只讨论了三个CSS属性;想想要考虑更多的CSS属性会有多么混乱。可见,CSS确实不太适合用来改变这些组件的外观和体验,但它仍让你能做些调整,如果你愿意忍受不同浏览器或不同操作系统上的不同的话。

我们将在下篇文章:[表单组件的属性兼容表]()中尝试指出哪些属性是可用的。

尽管CSS在复选框和单选框上的表现力已经够用了,但离其支持高级表单组件仍然遥遥无期。即使在<select>元素上有一些可能,但文件组件、日期选择器等仍不能被添加样式。

若你想获得对表单组件的完整控制权,你就得依赖Javascript,别无选择。在[怎样创建定制表单组件]()一文中,我们将了解如何自己实现它,而如今有一些很有用的库可以帮到你:

  • Uni-form是一个规范了表单格式和使用CSS给予表单样式的框架。在和jQuery一起使用时,它也提供了些额外的可选特性。

  • Formalize是一些常见Javascript框架(如jQuery、Dojo、YUI等)的一个插件,用于规范化和定制表单。

  • Niceforms是个提供了完整web表单定制的独立Javascript方法。你可以使用一些内建的主题、也可自己创建。

下面几个库则不止用于处理表单,但它们在处理HTML表单时有很多有趣的特性:

  • jQuery UI提供了些非常有趣的可定制高级组件,比如日期选择器(特别关注了无障碍访问)。

  • Twitter Bootstrap非常有用,如果你想规范化你的表单的话。

  • WebShim是一个庞大的工具,用于处理那些支持HTML5的浏览器。其web表单部分挺有用的。

要知道,绑定CSS和Javascript会引起副作用。所以若你选择了上述的一种库,就得时常保证在脚本失效时会可回退的样式表。造成脚本失效的原因很多,特别在移动端,故你的Web站点或app设计得能最好地处理这些情况。

当在HTML表单上使用CSS仍存在许多坑时,有很多方法可以绕过这些坑。本来是没有确切、通用的解决方案的,但现代浏览器带来了新可能。而现在,最佳方案是研究不同浏览器对用在HTML表单组件的CSS的支持程度。

下篇文章,我们将探索各种HTML表单组件对那些最重要的CSS属性的支持程度:[表单组件的属性兼容表]()。