HTML5的Web表单优化

Published on 2016 - 09 - 14

传统表单翻新

学习HTML5表单的最佳方式,莫过于找一个现有的例子,然后加以改进。图2展示了这样一个有待改进的例子。

这个表单的标记没有什么新意。如果你以前编写过表单,在这里就不会看到任何新玩艺儿。首先,整个表单被包装在一个<form>元素里:

<form id="zooKeeperForm" action="processApplication.cgi">
  <p><i>Please complete the form. Mandatory fields are marked with
  a </i><em>*</em></p>
  ...

<form>元素用于组织所有表单部件(也称为控件或字段),负责告诉浏览器把数据提交到哪里,方法是在action属性中提供一个URL。假如你只想在客户端使用JavaScript操作表单,那么只要在action属性里指定一个井号(#)即可。

注意 HTML5新增了一种机制,支持把表单控件放在它所在的表单外面。方法是使用新的form属性引用表单的ID值(如form="zooForm")。不过,如果浏览器没有实现这种机制,提交表单时就会忽略这些数据。换句话说,这个小的改进还不适合在真正的网页中应用。

像前面“动物园管理员申请表”这样精心设计的表单,都会使用<fieldset>元素划分几个逻辑块。每个块都有一个放在<legend>元素中的标题。以下是Contact Details部分的<fieldset>元素(其结果如图3所示):

...
<fieldset>
  <legend>Contact Details</legend>
    <label for="name">Name <em>*</em></label>
    <input id="name"><br>
    <label for="telephone">Telephone</label>
    <input id="telephone"><br>
    <label for="email">Email <em>*</em></label>
    <input id="email"><br>
</fieldset>
...

在任何表单里,通用的<input>元素都要承担大部分工作。通过<intput>可以收集文本,创建复选框、单选按钮和其他按钮。除了<input>元素,可以使用<textarea>元素让用户输入多行文本,而使用<select>元素则可以创建选择列表。想回顾这些表单元素的读者,可以参考表1。

控  件 HTML元素 说  明
单行文本框 <input type="text"> <input type="password"> 显示文本框,用户可以在其中填写内容。如果是密码类型的文本框,浏览器就不会显示用户输入的文本,而是用星号(*)或点号(• )来代替每个字符
多行文本框 <textarea>...</textarea> 显示大文本框,可以输入多行文本
复选框 <input type="checkbox"> 显示复选框,可以作为开关,选中或取消选中
单选按钮 <input type="radio"> 显示单选按钮(一个空心圆,可以选中或取消选中)。一般单选按钮都是成组出现的,每一组单选按钮都有相同的name 属性,用户只能选择其中一个
按钮 <input type="submit"> <input type="image"> <input type="reset"><input type="button"> 显示标准的可以单击的按钮,其中类型为submit的提交按钮用于收集表单数据,并将它们发送给指定目标;类型为image的图像按钮与提交按钮作用相同,但可以显示成一幅可以单击的图像而非按钮;类型为reset的重置按钮,用于清除用户的选择和已经输入的文本信息;而类型为button的按钮本身没有任何功能,但可以通过JavaScript 给它赋予功能
列表 <select>...</select> 显示一个选择列表,用户可以从中选择一或多个列表项。每个列表项用<option> 元素添加

以下是“动物园管理员申请表”的剩余标记,包含了一些新元素(如<select>列表、复选框和提交表单的按钮):

...
<fieldset>
  <legend>Personal Information</legend>
    <label for="age"><em>*</em>Age</label>
    <input id="age"><br>
    <label for="gender">Gender</label>
    <select id="gender">
      <option value="female">Female</option>
      <option value="male">Male</option>
    </select><br>
    <label for="comments">When did you first know you wanted to be a
     zoo-keeper?</label>
    <textarea id="comments"></textarea>
</fieldset>

<fieldset>
  <legend>Pick Your Favorite Animals</legend>
    <label for="zebra"><input id="zebra" type="checkbox"> Zebra</label>
    <label for="cat"><input id="cat" type="checkbox"> Cat</label>
    <label for="anaconda"><input id="anaconda" type="checkbox"> Anaconda
    </label>
    <label for="human"><input id="human" type="checkbox"> Human</label>
    <label for="elephant"><input id="elephant" type="checkbox"> Elephant
    </label>
    <label for="wildebeest"><input id="wildebeest" type="checkbox">
     Wildebeest</label>
    <label for="pigeon"><input id="pigeon" type="checkbox"> Pigeon</label>
    <label for="crab"><input id="crab" type="checkbox"> Crab</label>
  </fieldset>
  <p><input type="submit" value="Submit Application"></p>
</form>

访问本书在线示例网站:www.prosetech.com/html5,可以看到完整的示例及简单的样式表。其中,ZookeperForm_Original.html是传统的没有经过改进的表单,而ZookeperForm_Revised.html包含的则是使用HTML5表单元素之后的新表单。

注意 HTML表单有一个限制,就是不能修改浏览器呈现控件的方式。例如,你想把标准的傻里傻气的灰色复选框替换成一个又大又醒目的黑白框,再配上粗大的对勾图标。对不起,HTML不支持。(解决方案是使用JavaScript来创建一个与复选框具有相同行为的常规元素,换句话说,就是在用户单击该元素时来回切换这个元素的外观。)

HTML5仍然没有打破这个不能自定义的限制。对于需要自由定制控件样式和希望统一页面外观的人,表单不合适。

看完了这个真正的表单示例之后,接下来我们就要动手用HTML5来改进它了。以下几节先从小的占位符文本和自动获得焦点的字段开始。

通过占位符文本添加提示

表单一开始通常都是空的,但一堆空空如也的文本框看起来会让人心里发慌,特别是在文本框归属关系不清的时候尤其如此。所以,我们经常也可以看到一些文本框里预先带有一段提示性文本。这种占位符文本也叫做水印,因为这些文本的颜色一般是浅灰色的,用以区别用户真正输入的文本。图4展示了占位符文本。

要创建占位符,使用placeholder属性:

<label for="name">Name <em>*</em></label>
<input id="name" placeholder="Jane Smith"><br>
<label for="telephone">Telephone</label>
<input id="telephone" placeholder="(xxx) xxx-xxxx"><br>

不支持placeholder属性的浏览器会忽略它;IE的可能性最大。好在占位符文本并非不可或缺,没有它也不会影响到表单的基本功能。假如你真想要占位符文本,实际上也有很多JavaScritp补丁可以让IE支持,参见http://tinyurl.com/polyfills。

目前,还没有标准的统一方式来改变占位符文本的样式(例如把它们变成斜体或换成其他颜色)。但浏览器开发商最终会提供一个应用CSS样式的挂钩——事实上,你在看本书的时候,可能他们正在商讨具体的实现方案呢。如果你现在就有需求,可以使用特定于浏览器的伪类(即-webkit-input-placeholder和-moz-placeholder),不然,就只能听之任之了。

话说回来,使用被支持得更好的focus伪类,可以在文本框获得焦点时改变其样式。例如,可以让文本框的背景变成深色,以便更加显眼:

input:focus {
  background: #eaeaea;
}

用好占位符

并不是每个文本框都需要占位符,应该利用占位符来消除歧义。例如,“姓”这个文本框不用多加解释,而“名字”(如图4中的Name)就不是那么明确了。占位符文本告诉用户:名和姓之间要留一个空格。

在某些情况下,占位符则是一个示例值,即用户可能真地会输入的值。比如,Google的菜谱搜索(http://www.google.com/landing/recipes)框中使用chicken pasta作为占位符,说明应该输入菜名中包含的几个词,而不是做这道菜所需要的原材料。

另外,占位符可以用来表示值的格式。图4中的电话号码占位符(xxx) xxx-xxxx表示电话号码应该由三位数字的区号开头,后跟三位,再跟四位数字。这种占位符不一定表示不能接受其他格式的输入,但却能够给用户一个格式方面的建议。

应该避免用占位符去做两件事儿。一是不要用它代替字段描述或说明。比如,对于一个收集信用卡安全码的文本框,“您的卡背面的三位数字”并不适合以占位符形式出现。可以考虑把它放在输入框下面,或者把这句话作为title属性的值,当用户鼠标悬停到字段上时,弹出一个窗口来告诉用户应该输入什么:

<label for="promoCode">Promotion Code</label>
<input id="promoCode" placeholder="QRB001"
 title="Your promotion code is three letters
 followed by three numbers">

二是不要为了表示占位符不是真正的内容,就选择特殊字符作为占位符。比如,有些网站中使用[Jonh Smith]而不是John Simth,想用方括号强调这个占位符只是一个例子。这种做法很容易让人迷惑。

焦点:挑选正确的起点

加载完表单之后,用户要做的第一件事儿就是填写表单。然而,除非用户按下Tab切换到第一个控件,或者在其中单击一下鼠标,从而让第一个控件获得焦点,否则就不能输入。

在相应的<input>元素上通过JavaScript调用focus()方法,可以帮用户完成焦点切换。但这样就得编写代码,而且有时还会出现问题。比如,在调用focus()方法之前,用户已经单击了其他控件并开始输入的情况也可能发生。这时硬性地把焦点切换到第一个控件显得很粗鲁。但如果浏览器自己能控制焦点,它就可以在用户操作之前,先把焦点给予正确的控件。

这就是HTML5添加autofocus属性的初衷,但只能给一个<input><textarea>元素添加这个属性:

<label for="name">Name <em>*</em></label>
<input id="name" placeholder="Jane Smith" autofocus><br>

与placeholder属性一样,除了IE,其他浏览器都支持autofocus属性。同样,对于IE还是有办法的。可以使用Modernizr检测浏览器是否支持autofocus,然后自己编写脚本实现。也可以使用别人已经开发好的JavaScript程序(http://tinyurl.com/polyfills)。不过,很少有人为了这点功能如此兴师动众,除非你也想让IE支持其他表单功能,比如下面要讨论的数据验证。

验证:阻止错误

表单中的字段是为了从网页访客那里收集信息的。但是,无论你多么彬彬有礼地询问,都可能得不到想要的结果。性子急躁的或者糊里糊涂的访客,有可能跳过重要的部分,只填写少量信息,或者只是不小心按错了键。最后怎么样?他单击了“提交”按钮,你的网站收集到了一堆乱七八糟的数据。

很多网页都需要验证,就是在发生错误时捕捉到它(而更好的方案是防止出错)。很多年来,开发人员都要自己写JavaScript验证脚本,有时候也会用到专业的JavaScript库。应该说,这些验证方法效果都非常好。然而,鉴于验证是那么常用(可能要对每个人都做错误检查),验证解决的问题是那么集中(例如,发现无效的电子邮件或日期),以及编写验证脚本那么讨厌(没有人真心喜欢为每个表单都编写一遍同样的代码,更不要说测试这些脚本了),因此这个问题一定还有改进的空间。

HTML5规范的制定者决定让浏览器帮忙解决这些小问题,以后就不用让开发人员操心了。为此,他们设计了一套客户端验证方法(参见后面的“在两个地方验证”),让我们可以在<input>字段里嵌入常用的错误检查规则。而且,嵌入验证规则的方法很简单,只要指定正确的属性就行。

HTML5验证的原理

HTML5表单验证的基本原理就是你来告诉浏览器要验证哪个字段,但具体验证的细节,你不用管。这就像是提拔你当了领导,只不过没给你涨工资。

例如,你决定某个字段不能留空,意味着用户必须填上点什么。在HTML5中,可以通过required属性贯彻你这个指示:

<label for="name">Name <em>*</em></label>
<input id="name" placeholder="Jane Smith" autofocus required><br>

在两个地方验证

多少年来,敬业的开发人员已经找到很多解决验证问题的方法。今天,大家都有了明确的共识。那就是,要想让表单万无一失,就需要在两个地方验证。

  • 客户端验证。客户端验证就是在浏览器中检查错误,没有错误再提交。客户端验证的目的是减少填表人的麻烦。因为填表人要知道哪儿填得不对,不必填完30多个文本框再提交,而是随填随提示了。也就是说,可以在出错的字段旁边显示一条类似帮助的消息,提醒填表人在提交表单之前纠正错误。
  • 服务器端验证。这是在用户将数据提交给服务器之后进行的验证。此时,服务器端代码要负责检查所有细节,确保在进行下一步操作之前所有数据都是有效的。无论浏览器做不做验证,服务器端验证都是必不可少的。这是预防别有用心的人故意篡改数据的唯一途径。如果服务器端验证检测到问题,就向浏览器发回一个包含错误消息的页面。

简言之,客户端验证(包括HTML5表单验证)是为访客提供方便的,而服务器端验证才是确保数据正确性的。最关键的是,要知道这两个地方的验证缺一不可——除非你的表单极其简单,而且不担心数据有错误或者有错误问题也不大。

一开始并没有什么可见的提示告诉用户这是一个必填字段。为此,我们应该给出一些提示,比如为文本框应用不同的边框颜色或者在字段旁边放一个星号(就像前面“动物园管理员申请表”一样)。

当填表人单击提交按钮时,验证才发挥作用。如果浏览器支持HTML5表单,当它发现有一个必填字段为空时,它会拦截提交,并在无效字段旁边显示一个提示,如图5所示。

这里是相同的必填字段在Firefox(上)、Chrome(中)和Opera(下)中的样式。浏览器采用什么方式提醒用户,在规范里没有限制,但这三个浏览器都使用了类似提示条的弹出框。可惜的是,我们不能修改这个弹出框的样式,也不能修改验证消息——至少现在不能

下面几节会介绍,不同的属性表示不同的验证规则。可以给一个输入框应用多种规则,而一种规则可以应用给多个<input>元素(或者<textarea>元素)。提交表单之前,所有验证条件都必须满足。

而这就引发了一个有意思的问题:如果表单数据违反了多个规则会出现什么结果——比如有多个必填字段都空着?

同样,在用户填写完表单并单击提交之前,什么都不会发生。只有单击“提交”按钮,才会触发浏览器去从上到下地验证表单数据。只要发现一个无效的值,它就会停下来,不再继续验证其他字段。同时,它会取消提交操作,并在无效值的旁边显示一条错误消息。(此外,如果有问题的文本框不在当前视口中,则浏览器会滚动到它正好位于页面顶部。)用户纠正了输入错误并再次单击“提交”按钮后,浏览器会在下一个无效的值处停下来,再次给出错误提示。

注意 只有用户单击“提交”按钮,浏览器才会执行验证。这样可保证验证的效率,同时也比较适度,因而所有人都可以使用。
有些人喜欢在用户输入错误并(通过按Tab或单击网页中其他地方)离开相应字段时马上给出提示。对于比较长的表单,特别是用户可能会在多个不同字段中犯相同错误的情况下,这种方法很有必要。遗憾的是,HTML5不支持指定验证的时机,但将来倒是有这个可能。目前,如果想要即时显示验证消息,最好还是自己编写JavaScript或选择一个不错的JavaScript库。

关闭验证

有些情况下,可能需要禁用验证功能。比如,在测试服务器端代码能否适当处理无效数据的时候,就需要关闭客户端验证。要禁用整个表单的验证功能,可以在<form>元素中添加novalidate属性:

<form id="zooKeeperForm" action="processApplication.cgi" novalidate>

另外,也可以考虑增加另外一个提交按钮来绕过验证。这个办法有时候对网页是很有用的。比如,可以给出一个正式的提交按钮,强制对表单进行严格验证,而同时再给出另一个提交按钮,实现其他功能(如保存未完成的数据,以便将来使用)。要添加这个额外的按钮,可以在表示相应按钮的<input>元素中添加formnovalidate属性:

<input type="submit" value="Save for Later" formnovalidate>

好了,现在已经介绍了怎么通过验证来捕获缺失的信息。接下来,我们讨论如何查找不同类型数据中的错误。

注意 什么,你想验证数值?唉,没有验证规则强制文本中必须包含数字——不过,倒是有一个新的number数据类型。可惜的是,浏览器对这个新数据类型的支持还不好。

验证样式挂钩

虽然我们无法修改验证消息的样式,但却可以根据输入字段的验证状态(state)来改变它们的外观。比如,在输入的值无效时可以换一种背景颜色,只要浏览器一检测到问题,文本框的背景颜色就会立刻改变。

为此,只要使用几个新的伪类即可,可以使用的伪类如下。

  • required(必填)和optional(选填):根据字段中是否使用了required属性来应用不同的样式。
  • valid(有效)和invalid(无效):根据控件中是否包含错误来应用不同的样式。注意,除非访客提交表单,否则大多数浏览器并不会发现哪些值有效,哪些值无效;换句话说,访客不会实时看到表示输入无效的样式变化。
  • in-range(在范围内)和out-of-range(超出范围):根据控件的min和max属性判断输入值是否超出范围,从而为控件应用样式。

举个例子,假如你想为一个必填的<input>元素应用浅黄色背景,就可以为required伪类定义一条样式规则:

input:required {
  background-color: lightyellow;
}

或者,如果你只想突出显示那些必填且当前填入了无效值的字段,那么可以像下面这样把required和invalid伪类组合起来:

input:required:invalid {
  background-color: lightyellow;
}

有了这条规则,空着的字段就会自动高亮,因为这些字段违反了必填字段的规则。

可以灵活运用上述技巧,比如组合valid伪类与focus伪类,或者为表示无效的值使用一个带错误图标的偏移背景。当然,还有一个忠告:可以使用这些伪类增强页面体验,但要保证页面没有它们也照样很好——旧版本的浏览器不支持!

使用正则表达式

HTML5支持的最强大(也最复杂)的验证方法是正则表达式。既然JavaScript支持正则表达式,那么为HTML表单添加这项功能也在情理之中。

所谓正则表达式,就是一种用正则表达式语言编写的文本模式。正则表达式的用途是匹配文本——比如,可以用正则表达式验证邮政编码中包含正确的字母和位数,或者验证电子邮件地址中包含一个@符号和一个至少两个字母的域名后缀。还是看一个正则表达式吧:

[A-Z]{3}-[0-9]{3}

开头的方括号定义了允许的字符范围。换句话说,[A-Z]就是允许从A到Z的任意字母。随后的花括号表示允许几个字符,{3}当然表示允许3个大写字母。接下来的短横线没有特殊的含义,就表示三个大写字母后面必须有一个短横线。最后,[0-9]表示允许一个0到9的数字,而{3}要求必须是3个数字。

正则表达式经常用于搜索(在长文档中查找匹配模式的文本)和验证(验证某个值匹配模式)。HTML5表单使用正则表达式来验证。

注意 正则表达式极客请听好——不必使用^和$字符表示要匹配字段值的开头和结尾。HTML5会自动确保这一点。实际上,这就是说正则表达式匹配的是字段中完整的值,验证的也是整个值的有效性。

好,下面这些值都是有效的,因为它们与上面的模式匹配:

QRB-001
TTT-952
LAA-000

但下面这些值无效:

qrb-001
TTT-0952
LA5-000

常用的正则表达式很可能比这个例子所展示的复杂。而且,编写正则表达式本身也很烦琐。正因为如此,大多数开发人员都愿意搜索一个现成的正则表达式来验证相关的数据类型。要不然,也会找别人帮忙。

找到或写好一个正则表达式之后,可以通过pattern属性将其应用到<input><textarea>元素:

<label for="promoCode">Promotion Code</label>
<input id="promoCode" placeholder="QRB-001" title=
 "Your promotion code is three uppercase letters, a dash, then three numbers"
 pattern="[A-Z]{3}-[0-9]{3}">

图6展示了输入的值违反正则表达式规则后的结果。

提示 浏览器不会验证空值。在这个例子中,不输入折扣号(Promotion Code)可以通过验证。如果这不是你希望的结果,那么就要同时指定pattern和required属性。
注意 正则表达式似乎能完美地匹配电子邮件地址(确实也是)。尽管如此,建议大家也不要用正则表达式,因为HTML5专门定义了一个用于输入电子邮件地址的输入类型,当然已经内置了正确的正则表达式。

自定义验证

HTML5规范也规定了一组JavaScript属性,通过它们可以知道字段是否有效(或强制浏览器验证这些字段)。其中最常用的是setCustomValidity()方法,基于这个方法可以针对特定字段编写自定义的验证逻辑,并利用HTML5的验证机制。

下面来看看怎么自定义验证。首先,要检查相应字段是否有错,为此需要处理onInput事件,没有什么要解释的:

<label for="comments">When did you first know you wanted to be a
 zookeeper?</label>
<textarea id="comments" oninput="validateComments(this)" ></textarea>

这里,onInput事件会触发一个名为validateComments()的函数。这个函数的代码是你自己写的,主要是检测<input>元素的值,然后调用setCustomValidity()方法。

如果当前值有问题,那么在调用setCustomValidity()方法时就需要提供一条错误消息。否则,如果当前值没有问题,调用setCustomValidity()方法时只要传入空字符串即可;这样会清除以前设置过的自定义错误消息。

要强制评论(comment)框中至少有20个字符,可以这样写validateComments()函数:

function validateComments(input) {
  if (input.value.length < 20) {
    input.setCustomValidity("You need to comment in more detail.");
  }
  else {
    //没有错误。清除任何错误消息
    input.setCustomValidity("");
  }
}

图7展示了违反上述规则并提交表单的结果。

如果在调用setCustomValidity()方法时提供了错误消息,浏览器会将该消息当做自己内置的消息。提交表单时,就会看到弹出的警告框中包含自定义的错误消息

当然,对于验证需要长字符串的字段,使用正则表达式可能更简单明了。而尽管正则表达式很适合验证某些数据类型,自定义验证逻辑却适用于任何情况,无论是复杂的数学计算还是与Web服务器通信。

注意 网页访客可以看到你放在JavaScript中的一切,这里没有什么加密算法。就拿前面示例中的折扣码来说,其位数是12位。但你肯定不愿意在自定义验证逻辑中透露这个信息,否则无疑会为那些想要伪造折扣码的人提供便利。对于这种情况,还是把验证逻辑放在服务器端比较好。

浏览器对验证的支持

不同浏览器对验证功能的支持也不一样。换句话说,有的浏览器支持某些验证功能,而另一个浏览器则未必支持同样的功能。表2列出了支持到目前为止介绍的所有验证功能的浏览器及它们的最低版本。

版本 IE Firefox Chrome Safari Opera Safari iOS Android
最低版本 10* 4 10 5(仅限Windows) 10

由于HTML5验证不能取代服务器端验证,因此可以仅将其看做一种增强;在这种情况下,浏览器支持的一致不一致也就无所谓了。此时,对于不支持这些验证的浏览器(如IE9),用户提交的数据会直接发送给服务器,然后服务器发现其中的问题,再返回带错误消息的相同页面。

另一种可能是,你的网站包含一些复杂的表单,有些到底该怎么填也不太明确,而你也不愿意眼睁睁看着大量IE用户流失。此时,有两个选择:一是你自己编写验证功能,二是使用现成的JavaScript库,借助外脑。到底怎么办,取决于验证任务的广度和复杂性。

假如你的表单只需少量简单的验证,你可以自己写验证代码。利用Modernizr可以检测浏览器对各种HTML5表单验证功能的支持情况。例如,使用Modernizr.input.pattern属性可以检测出浏览器是否支持pattern属性:

if (!Modernizr.input.pattern) {
  //浏览器不支持正则表达式验证,可以在JavaScript中使用正则表达式
  ...
}

注意 这里的pattern属性只是Modernizr.input对象中的一个属性。其中,用于检测浏览器对表单验证支持情况的属性还有:placeholder、autofocus、required、max、min和step。

没错,这个例子没有告诉你什么时候执行检测,也没有告诉你该如何反馈。假如你想模仿HTML5验证机制,可以在用户提交表单的时候执行检测。为此,需要为表单的onSubmit事件定义处理函数,根据情况返回true(表示验证通过,可以提交表单)或false(表示验证未通过,浏览器应该取消提交操作):

<form id="zooKeeperForm" action="processApplication.cgi"
 onsubmit="return validateForm()">

以下是一个简单的示例,演示了针对一个必填字段的自定义验证代码:

function validateForm() {
  if (!Modernizr.input.required) {

    //不支持required属性,因此必须自己编写代码检测

    //首先,取得包含所有元素的数组
    var inputElements = document.getElementById("zooKeeperForm").elements;

//接着,遍历数组,检测每个元素
    for(var i = 0; i < inputElements.length; i++) {

      //检测当前元素是否必填
      if (inputElements[i].hasAttribute("required")) {
        //如果是必须填写的,则检测其值是否为空
        //如果为空,则表单验证失败,返回false
        if (inputElements[i].value == "") return false;
      }
    }

    //如果到了这儿,则一切顺利
    //浏览器可以提交表单
    return true;
  }
}

提示 以上代码运用了基本的JavaScript技术,包括查找元素、循环和条件逻辑。有关JavaScript的基本知识,请参阅附录B。

如果你的表单很复杂,而你又想省点事儿(省下的时间可以学习未来的技术),则大可选择一个现成的JavaScript库。从技术角度讲,无论是自己写,还是使用JavaScript库都没有什么不一样。都是先检测浏览器对验证功能的支持情况,然后再在必要时手工验证。但区别在于,JavaScript库已经为你准备好了所有代码。

几个特殊的输入属性

HTML5还定义了另外几个不用于验证的属性,而是用于在编辑表单时控制浏览器的行为。这些属性并不是所有浏览器都支持。因此,这几个属性目前还只能用于试验。

  • Spellcheck。有些浏览器可以帮用户检查输入的拼写是否正确。不过有一个明显的问题,即并非所有输入都是单词,而这个功能只允许用户“胡乱”输入几个字母。将spellcheck设置为false,表示不建议浏览器对字段进行拼写检查;设置为true,表示建议拼写检查。(浏览器默认的拼写检查行为也不一样,如果你不设置spellcheck属性,那么这个问题就会出现。)
  • Autocomplete。有些浏览器为了节省你的时间,会在你向字段中输入信息时提供最近输入的值供你选择。自动完成功能并不是所有时候都适用的,正如HTML5规范中指出的,有些信息属于敏感信息(比如,核攻击的编码),而有些信息只是临时性的(如一次性的银行登录验证码)。在这种情况下,应该把autocomplete属性设置为off,告诉浏览器不要提供自动完成的建议。而在确实需要的情况下,也可以把autocomplete设置为on。
  • Autocorrect和autocapitalize。这两个属性可以用来在移动设备(即iPad和iPhone中的Safari)上控制自动纠错和自动大小写功能。
  • Multiple。很久以来,Web开发人员一直通过为<select>元素添加multiple属性,达到让用户能选择多个列表项的目的。但现在,可以为某些类型的<input>元素添加这个属性,包括用于上传文件的file类型和email类型。在支持的浏览器中,用户可以选择多个文件一块上传,或者可以在一个输入框中贴上多个邮件地址。

新的输入控件

HTML表单有一个奇怪的做法,即用一个元素(含含糊糊地叫<input>)创建多个控件:复选框、文本框,以及按钮。此时,type属性就成为地地道道的总开关,它的值决定了<input>元素到底是什么控件。

如果浏览器遇到了不认识的<input>元素类型(type属性值),它就将其作为一个普通的文本框来处理。换句话说,以下3个元素在浏览器中都会生成一个文本框:

<input type="text">
<input type="super-strange-wonky-input-type">
<input>

HTML5利用了浏览器的这个默认处理方式,为<input>元素添加了新的类型,如果浏览器不认识这些类型,仍然会将其当做普通的文本框来处理。比如,要创建一个用于输入电子邮件地址的文本框,可以使用新的email类型:

<label for="email">Email <em>*</em></label>
<input id="email" type="email"><br>

在不支持email类型的浏览器(如IE9)中打开这个网页,会看到一个普通文本框,这是完全可以接受的。但支持HTML5表单的浏览器会更聪明一点,它们会像下面这样做。

  • 提供便于编辑的辅助。例如,智能一些的浏览器可以从你的地址簿中取得电子邮件地址,帮你填到电子邮件字段中。
  • 限制可能出现的错误。例如,在数值文本框中输入的字母会被浏览器忽略,或者无效的日期会被拒绝(当然,也可能会要求你从一个迷你小日历中选择日期,这样既方便又可靠)。
  • 执行验证。在单击提交按钮时,浏览器可以执行更加完善的检查。比如,智能一些的浏览器会发现电子邮件字段中明显存在错误的邮件地址,从而拒绝继续提交。

HTML5规范没有就上述第一点作出明确规定。浏览器开发商可以视情况自行决定如何显示和编辑不同的数据,而不同的浏览器也可以有自己的特色功能。比如,移动设备上的浏览器可以定制虚拟键盘,显示或隐藏不需要的键(参见图8)。

在移动浏览器中,要填写表单可没有全键盘可以使用。图中的iPod通过定制虚拟键盘为用户提供了方便,根据要输入的数据类型——电话号码和电子邮件,会分别显示数字键盘(左)和带有@按键及小空格键的字母键盘(右)

不过,更重要的还是预防错误和检测错误的功能。最起码,支持HTML5表单的浏览器在发现表单中包含违反数据规则的数据时,要阻止表单提交。所以,如果浏览器不能做到预防错误(即上述第二点),那它必须在用户提交表单时验证数据(上述第三点)。

然而,目前并非所有浏览器都达到了这些要求。有的支持新控件类型,也提供了一些编辑辅助,但缺少验证功能。而很多只支持其中部分新控件,且不同浏览器支持的范围又不一样。移动浏览器的问题最多,虽然它们提供编辑辅助功能,但却不会对数据进行验证。

表3列出了新的控件类型以及完全支持它们的浏览器——完全支持是指在有数据违反规则时,浏览器会阻止表单提交。

控件类型 IE Firefox Chrome Safari Opera Safari iOS** Android
email 4 10 5(仅限Windows) 10.6
url 4 10 5(仅限Windows) 10.6
search* n/a n/a n/a n/a n/a n/a n/a
Tel* n/a n/a n/a n/a n/a n/a n/a
number 10 5(仅限Windows)
range 6 5 11
datetime 10 11
color 11
  • * HTML5规范没有要求验证这种类型的数据。
  • ** 虽然iOS上的浏览器不提供验证,但其定制的虚拟键盘(参见图8)能提供极大的便利,因此还是有必要使用特定类型的控件。

提示 如果你使用Modernizr,可以检测Modernizr.inputtypes对象的属性来确定浏览器支持的控件类型。比如,如果Modernizr.inputtypes.range返回true,则说明浏览器支持range类型的控件。

电子邮件地址

电子邮件地址使用email类型。一般来说,有效的电子邮件地址是一个字符串(当然,有些字符是不允许出现的)。这个字符串中必须包含@符号和一个点号,而且两者之间至少要间隔一个字符,点号后面至少也要有两个字符。以上差不多就是一个有效电子邮件地址的验证规则了。可是,要为验证电子邮件地址编写代码或者正则表达式,就没有这么简单了。这件看似简单的任务难倒过很多善意的开发人员。因此,最好还是找一个支持email控件的浏览器,让它自动帮我们检测得了(参见图9)。

电子邮件控件支持multiple属性,添加了这个属性后就可以在同一个字段里输入多个电子邮件地址。不过,多个电子邮件地址之间只有逗号分隔,看起来仍然像一个字符串。

注意 再提醒一次,空值可以通过验证。如果你想强制用户必须输入有效的电子邮件地址,就要在email控件中指定required属性。

网址

网址使用url类型。说到网址是由什么组成的,可能会引发激烈的讨论。但大多数浏览器都会对验证网址采用粗略的算法。首先要有一个URL前缀(可以是合法的,如http://;也可以是编造的,如bonk://),然后可以是空格和大多数特殊字符(冒号除外)。

有些浏览器也会在网址控件中给出URL建议,这些建议项一般是从浏览器最近的历史记录中提取的。

搜索框

搜索框使用search类型。搜索框中通常要输入关键词,用于执行某种搜索。可能是搜索整个互联网,也可能是搜索一个网页或者对自己的某些信息执行定制搜索。无论如何,搜索框的样子与行为都与常规的文本框没有太大区别。

在Safari等浏览器中,搜索框的样式可能会稍有不同——两端都是圆形。而在Safari和Chrome的搜索框中输入关键词时,一个X图标就会立刻出现在搜索框的右侧,单击就可以清除搜索框。除了这些细微的差别之外,搜索框与文本框无异。但搜索框的值是有其特定语义的。换句话说,使用搜索框可以让浏览器及辅助(残障人士)上网的软件知道它是干什么用的。也许将来会有一天,这些工具能够利用搜索框把访客引导到正确的位置,或者为用户提供一些便利功能。

电话号码

电话号码使用tel类型。电话号码有很多种模式,有的只包含数字,有的还会包含空格、短横线、加号和圆括号。正是因为存在这么多差异,HTML5规范没有要求浏览器验证电话号码。不过,谁都知道电话号码字段至少不能接受字母(当然,tel控件确实不接受字母)。

目前,使用tel类型控件的唯一用途是在移动浏览器中定制虚拟键盘,键盘中只包含数字,没有字母。

数值

HTML5定义了两种数值类型的控件。其中,number类型用于常规数值。

使用number类型的控件有明显的好处。常规文本框什么值都可以接受:数值、字母、空格、标点符号,以及一些专门的卡通的字符。为此,检测输入的值是不是数值以及是不是在某个范围内就成了非常重要的任务。现在有了number类型的控件,浏览器就可以自动忽略非数值字符。看一个例子吧:

<label for="age">Age<em>*</em></label>
<input id="age" type="number"><br>

当然,数值也有很多种,也并非任何数据形式都可以接受任意数值。上面标记中所示的年龄(age)可以接受43 000、-6之类的值。为了增加限制,需要配合使用min和max属性。比如,下面的例子就把可接受的数值限制在了0到120之间:

<input id="age" type="number" min="0" max="120"><br>

一般来说,number控件只接受整数,不接受30.5这样的小数。(实际上,有些浏览器都不允许输入小数点。)不过,通过设置step属性可以改变这一点;step属性表示可以接受的数值之间的间隔。例如,将最小值(min)设置为0,将间隔值(step)设置为0.1,意味着可以输入0、0.1、0.2、0.3……然而,输入0.15后提交表单就会收到错误消息。默认的间隔值为1。

<label for="weight">Weight (in pounds)</label>
<input id="weight" type="number" min="50" max="1000" step="0.1"
value="160"><br>

设置step属性也会影响到数值框的微调按钮,如图10所示。

很多浏览器都会在数值框中添加微调按钮。每单击一次向上箭头,数值都会增加step属性指定的值(除非已经达到允许的最大值)。类似地,每单击一次向下箭头,数值都会减少step属性指定的值

滑动条

HTML5的另一个数值类型的控件是range。与number控件类似,它也可以表示整数或者小数值。同样,range控件也支持与number控件相同的属性(min和max),用于设置允许的范围。下面是一个例子:

<label for="weight">Weight (in pounds)</label>
<input id="weight" type="range" min="50" max="1000" value="160"><br>

两者的区别是range控件用滑动条的形式表示信息。智能浏览器对于range控件,会显示一个如图11所示的滑动条,而不是文本框。

这个range控件与我们熟悉的音量调节器很像,它非常适合在最小值和最大值已知、范围适中且输入的特定值并不重要(但该值接近最小值还是最大值重要)的情况下使用

要设置range控件的值,只要把滑块拖动到合适位置即可,也就是把滑块放在滑动条上最大值和最小值之间的某个地方。支持range控件的浏览器不会告诉你最终设定了多大的值。如果你想显示这个值,可以使用JavaScript响应滑动条的变化事件(即处理onChange事件),然后在旁边把值显示出来。当然,你得事先检测一下浏览器是不是支持range控件(使用Modernizr等工具)。如果浏览器不支持range控件,就没有必要多此一举了,因为结果只能是在文本框中输入值。

日期和时间

HTML5定义了几个与日期有关的新控件。支持日期控件的浏览器会提供一个方便的下拉式日历,供用户选择。这样,不仅可以避免对日期格式的困惑,也可以避免意外(或有意)输入一个不存在的日期。智能的浏览器还能提供更多便利,比如与个人日历集成。

目前来看,虽然日期控件很有用,但支持浏览器对它的支持还不好。Opera是唯一一个提供下拉式日历的浏览器(如图12所示)。Chrome的支持最简单,只提供一个带微调按钮的文本框(类似number类型),但不接受错误的日期格式。
 

date和time控件的文本框(左)有些不一样。但真正方便的地方在于,Opera提供了下拉式日历,让用户可以选择适当日期,而不必担心格式(右)

表4中列出了6种新的日期时间型控件。

控件类型 说  明 示  例
date 格式为YYYY-MM-DD 的日期 2012-01-25 表示2012年1月25日
time 格式为HH:mm:ss.ss ,用24小时制表示的时间,秒的部分可选 14:35或14:35:50.2 表示下午2:35(50.2秒)
datetimelocal 格式为YYYY-MM-DDTHH:mm:ss ,包含日期和时间,中间以大写T分隔 2012-01-15T14:35 表示2012年1月15日下午2:35
datetime 格式为YYYY-MM-DDTHH:mm:ss-HH:mm,包含日期和时间,还有一个时区偏移量;与<time> 元素格式相同 2012-01-15T14:35-05:00 表示美国纽约(西五区)时间2012年1月15日下午2:35
month 格式为YYYY-MM ,表示年月 2012-01 表示2012年1月
week 格式为YYYY-Www ,表示年和周;根据年份不同,一年可能有52周或53周 2012-W02 表示2012年第二周

提示 支持日期类型的浏览器也支持min和max属性。换句话说,可以设置最小和最大日期,但日期格式必须正确。比如,要限定某日期字段中必须填写2012年的日期,可以这样写:<input type="date" min="2012-01-01" max="2012-12-31">

颜色

颜色使用color类型。虽然用处不大,但这个新控件很有意思,可以让用户从下拉式色盘中选取颜色,这个色盘就像在桌面绘图程序中看到的一样。目前,Opera是唯一提供下拉色盘的浏览器。在其他浏览器中,用户必须手工输入十六进制的颜色编码(当然,你也可以考虑使用html5Widgets库)。

新元素

迄今为止,我们介绍了HTML5对表单的扩展,介绍了新的验证功能,也介绍了通过添加新的控件让表单更加智能。这些新功能都是很实用的,也得到了广泛支持。但是,这些并不是HTML5表单的全部。

HTML5也新增了一些全新的元素,用于弥补缺漏和增加功能。有了这些新元素,就可以在网页中添加下拉建议项、进度条、工具栏等。这些新元素的问题在于,旧版本的浏览器肯定不支持它们,而鉴于HTML5规范本身还在制定之中,新浏览器也没有急着支持它们。因此,本文只介绍那些已经得到支持的功能。不少读者好奇能用这些元素干什么,但是却不能现在就把它们派上用场,除非你对对付浏览器怪癖和不兼容性上瘾。

使用<datalist>显示输入建议

新的元素可以让你在普通文本框中添加一个下拉建议列表。这样,填表的人既可以直接从列表中选择输入,也可以自由输入(参见图13)。

输入的同时,浏览器会显示出匹配的建议项。例如,输入字母“ca”,浏览器就会显示名字中包含这两个字母(不一定在开始位置)的动物

<datalist>必须配合一个标准的文本框使用。假设我们有以下<input>元素:

<legend>What's Your Favorite Animal?</legend>
<input id="favoriteAnimal">

要为这个文本框添加建议项列表,必须先创建一个<datalist>。从技术角度讲,可以在任何地方定义这个列表,因为<datalist>不会显示出来,而只会为使用它的文本框提供数据。话虽这么说,还是把这个<datalist>放在使用它的<input>元素之后(或之前)更合适。下面就是一个<datalist>元素的示例:

<datalist id="animalChoices">
  <option label="Alpaca" value="alpaca">
  <option label="Zebra" value="zebra">
  <option label="Cat" value="cat">
  <option label="Caribou" value="caribou">
  <option label="Caterpillar" value="caterpillar">
  <option label="Anaconda" value="anaconda">
  <option label="Human" value="human">
  <option label="Elephant" value="elephant">
  <option label="Wildebeest" value="wildebeest">
  <option label="Pigeon" value="pigeon">
  <option label="Crab" value="crab">
</datalist>

与原来的<select>元素一样,<datalist>也使用<option>定义数据项。每个<option>表示一个可供选择的建议,其label属性是显示在文本框中的内容,而value属性是最终会发送给服务器的值(如果用户选择了该项)。就其本身而言,<datalist>是完全不可见的。为了将它与文本框联系起来以便提供建议,下一步就需要将<input>元素的list属性设定为<datalist>的ID:

<input id="favoriteAnimal" list="animalChoices">

在支持<datalist>的浏览器中——目前只有Opera 10和Firefox 4+,访客可以看到如图13所示的结果。不支持的浏览器会忽略list属性和<datalist>元素,所有建议项也就白定义了。

但也不尽然,我告诉大家一个不错的后备技巧,可以让其他浏览器也能利用这些数据。技巧就是在<datalist>中再添加另一个元素。这个技巧之所以可行,是因为支持<datalist>的浏览器只会关注其中的<option>元素,而会忽略其他内容。下面这个修改后的例子就利用了这一点。

<legend>What's Your Favorite Animal?</legend>
<datalist id="animalChoices">
  <span class="Label">Pick an option:</span>
  <select id="favoriteAnimalPreset">
   <option label="Alpaca" value="alpaca">
    <option label="Zebra" value="zebra">
    <option label="Cat" value="cat">
    <option label="Caribou" value="caribou">
    <option label="Caterpillar" value="caterpillar">
    <option label="Anaconda" value="anaconda"><option label="Human" value="human">
    <option label="Elephant" value="elephant">
    <option label="Wildebeest" value="wildebeest">
    <option label="Pigeon" value="pigeon">
    <option label="Crab" value="crab">
  </select>
  <br>
  <span class="Label">Or type it in:</span>
</datalist>
<input list="animalChoices" name="list">

而这样一来,支持<datalist>的浏览器仍然只会显示一个文本框和一个下拉建议项列表(与图13显示的一样)。而在其他浏览器中,新添加的标记会把<datalist>的那些建议项组织成一个选择列表,并允许用户选择、输入(如图14所示)。

这种过渡完全没有痕迹。只不过在服务器端接收到表单数据后,需要判断数据是来自选择列表(即这里的favoriteAnimalPreset)还是来自文本框(即favoriteAnimal)。虽然会多费这一点点周折,但毕竟为用户提供了很大的方便,没有抛弃任何人。

注意 最初引入<datalist>元素的时候,还为它设计了一个从其他地方(如Web服务器,然后可能再访问数据库)取得数据的功能。在HTML标准未来的版本中,可能还会正式增加这项功能。不过现在要想实现这项功能,恐怕还只能自己写JavaScript代码,利用XMLHttpRequest对象来获得数据。

进度条和计量条

另外两个新图形微件是<progress><meter>,这两个元素外观相似,作用不同(参见图15)。
 

在支持的浏览器中,<meter><progress>能够提供形象的度量(左)。而在其他浏览器中,则会显示你设定的后备内容(右)

其中,<progress>表示任务的进度,背景为灰色,完成的部分填充为脉动式绿色条。说起这个形象,大家可能都很熟悉,Windows操作系统中复制文件时就会出现这种进度条。不过,用户查看网页的浏览器不同,进度条的样子也可能不同。

<meter>元素表示的是位于已知范围内的一个值。乍一看,<meter><progress>的外观相同,但实际上绿色条阴影更深一些,而且没有脉动效果。根据浏览器不同,计量条的颜色可能会在值“过低”或“过高”的时候改变。比如,图中后一种情况下,Chrome把绿条变成了黄条。但<meter><progress>最大的不同,还是标记要表达的语义。

注意 严格来讲,新的<meter><progress>元素并非必须出现在表单中。实际上,它们甚至都不是真正的控件(因为它们不能从网页访客那里收集信息)。可是,官方的HTML5规范把它们归为一类,也是考虑<meter><progress>元素给人的感觉像是控件(以图形的方式显示数据)。

当前,Chrome 9+、Opera 11+和Safari 5.1+支持<meter><progress>元素。Firefox和IE暂时还不支持它们。

使用<meter><progress>很简单。先来看看<progress>,它有一个value属性,用于设置表示进度的百分比(即填充的绿色条的宽度),值其实是0到1之间的小数。比如,可以用0.25来表示完成了进度的25%:

<progress value="0.25"></progress>

另外,也可以利用max属性设置最大值,改变进度条的比例。例如,max设为200,那么value就要位于0和200之间。如果把value设为50,那么结果就和前面例子中将value设置为0.25(25%)一样:

<progress value="50" max="200"></progress>

这个比例其实就是为了让人看着方便。而浏览网页的人也不会看到进度条中实际的值。

注意 实际上,<progress>元素只是一种显示立体进度条的便捷方式,这个元素本身什么也不会做。比如,要是想利用进度条来显示后台任务的进度,那么你必须自己编写JavaScript代码取得<progress>元素并实时更新它的值。

不支持<progress>元素的浏览器会忽略它。作为后备,可以在这个元素内部放置进度值,如:

<progress value="0.25">25%</progress>

记住,在支持<progress>元素的浏览器中,是不会显示这个后备内容的。

除了实际显示进度的进度条,还有另外一种进度条——不确定进度条。不确定进度条表示反正有后台任务,但到底什么时候完成不知道(可以想象那些永远也不停转的GIF图)。不确定进度条也是灰色背景,但不断会有绿色闪过,从左到右。要创建不确定进度条,只要不设置value值即可:

<progress>Task in progress ...</progress>

<meter>元素大致也一样,只不过它表示的是某种计量,因此也被称为计量器。一般来说,给<meter>元素设置的值都会对应现实中的某个值(比如,钱数、天数或重量)。为了控制<meter>元素显示这些数据的方式,需要设置一个最大值和一个最小值(使用max和min属性):

Your suitcase weighs: <meter min="5" max="70" value="28">28 pounds</meter>

同样,位于<meter>元素开始与结束标记之间的内容,只会在不支持该元素的浏览器中显示。当然,有时候把<meter>元素的值显示出来也是必要的。此时,你得自己把这个值添加到页面中,不要依赖提供后备内容的方式。下面的代码展示了相应的做法,即先显示出所有信息,然后再添加一个可选的<meter>元素(只有支持它的浏览器才会显示):

<p>Our goal is to raise $50,000 for SLF (Save the Lemmings Foundation).</p>
<p>So far we've raised $14,000. <meter max="50000" value="14000"></meter>

为了让<meter>元素能够表示那些“过高”或“过低”的值,而且还能表示得恰如其分,就需要用到low和high属性。大于high(但小于max)的值,就说明超过了它应有的大小了,但仍然是可以接受的。类似地,小于low(但大于min)的值就是过低了:

Your suitcase weighs:
<meter min="5" max="100" high="70" value="79">79 pounds</meter>*
<p><small>* A surcharge applies to suitcases heavier than 70 pounds.
</small></p>

有些浏览器可能不会利用这些信息。比如,Chrome对于过高的值会显示黄条(参见图15),但对于过低的值则没有任何变化。最后,还可以使用optimum属性将某个值标记为理想的值,但这个属性不会影响计量器在当前浏览器中的显示结果。

总之,只要浏览器支持,<progress><meter>就可以为用户带来一些便利。

使用<command><menu>创建工具条和菜单

这个功能也许是所有未实现功能中最有用的。设计思路就是通过一个元素(<command>)来表示用户可以执行的操作,而用另一个元素(<menu>)来封装这组操作。灵活组织这两个元素并为它们设置适当的样式,可以利用<menu>把Mac桌面下方的可停靠工具条搬到浏览器窗口中来,或者创建出单击后显示的弹出式上下文菜单。可是,现在还没有浏览器支持这两个元素,所以要知道它们会不会带来我们想象的效果,只能拭目以待。

网页中的HTML编辑器

HTML5奉行“修补牛蹄子路”的原则。意思就是说,把今天开发人员使用的未标准化的功能,正式写入HTML5标准。这方面的一个例子就是标准化了两个奇怪的属性:contentEditable和designMode。这两个属性的作用是将浏览器转换成简单的HTML编辑器。

这两个属性早就已经有了。事实上,它们是在原先IE一统天下的时候,由IE5率先引入的。随着越来越多Windows扩展的出现,大多开发人员都不再使用这两个属性了。但随着时间推移,其他浏览器也陆续支持了IE实用但又怪异的富HTML编辑功能。今天,所有桌面浏览器都支持这两个从未写进任何标准的属性。

使用contentEditable编辑元素

下面要介绍的第一个能帮我们实现HTML编辑功能的属性是contentEditable。把这个属性添加到任何元素,都可以使该元素的内容可以编辑:

<div id="editableElement" contentEditable>You can edit this text, if you'd
like.</div>

乍一看,可能还看不出有什么不同。但加载完网页后再单击这个<div>元素的内容,你就会发现文本编辑光标(插入光标),如图16所示。

单击可编辑区域后,就可以通过键盘上的前后左右键移动光标,可以删除文本,也可以插入内容(左)。还可以按Shift键选择并复制、剪切、粘贴文本(右)。很像在Word中编辑文本,只是不能超过<div>的界限

何时使用HTML编辑功能

在尝试富HTML编辑功能之前,有必要先搞清楚这个功能到底有什么用。除了能带来新奇感之外,能编辑HTML实际上对任何人都没有什么吸引力。除非你需要向用户提供一种简单快捷的编辑HTML内容的方式,比如让用户能添加博客文章、输入评论、发布分类广告或者编写发送给其他用户的消息。

即使你确定需要这种功能,contentEditable和designMode属性也未必是第一选择。因为它们不能提供真正的网页设计工具所具备的那些好用的功能,比如修改标记、查看和编辑HTML源代码、拼写检查,等等。使用HTML的编辑功能,确实可以构建更好用的编辑器,但需要做一些额外的工作。可是,如果你真需要富文本编辑功能,恐怕还是选择别人已经做好的编辑器更方便,只要把相应代码插入网页中即可。

在这个例子中,可编辑的<div>中只包含文本,但其实可以把任何元素放入其中。就算是把整个页面放到里面,让用户可以编辑整个页面也没有问题。类似地,要想让页面中几个不同部分可以编辑,只要为相应元素应用contentEditable属性即可。

提示 有些浏览器支持少量的内置命令。比如,在IE中使用快捷键Ctrl+B、Ctrl+I和Ctrl+U可以为文本加粗、加斜体和加下划线。类似地,在Firefox中,按Ctrl+Z可以撤销上一次操作。而在Chrome中可以使用前述所有命令。。

通常,我们都不会在标记中设置contentEditable属性,要设置也是通过JavaScript,并且在编辑完成后再取消可编辑的功能。下面这两个函数就是用来开启和关闭编辑功能的。

function startEdit() {
  //让元素可以编辑
  var element = document.getElementById("editableElement");
  element.contentEditable = true;
}

function stopEdit() {
  //把元素修改为正常状态
  var element = document.getElementById("editableElement");
  element.contentEditable = false;

  //在消息框中显示标记
  alert("Your edited content: " + element.innerHTML);
}

以下两个按钮用于触发它们:

<button onclick="startEdit()">Start Editing</button>
<button onclick="stopEdit()">Stop Editing</button>

不要把这两个按钮放到网页的可编辑区域中!因为网页一变得可以编辑,其中的元素就不会产生事件,因而就无法再触发JavaScript代码了。

图17展示了元素变成可编辑之后和为其中内容应用一些样式后的结果(拜Ctrl+B命令所赐)。

注意 不同浏览器中的富HTML编辑功能也会有一点差异。例如,在Chrome中按Ctrl+B会为元素添加<b>标签,而在IE中则会添加<strong>标签。而在按回车键换行和按退格键删除标签时,也会出现差异。说到这,就不难理解HTML5标准化富HTML功能的的意义了,至少可以让不同浏览器的行为一致。

使用designMode编辑页面

与contentEditable属性类似,但designMode属性能够让用户编辑整个页面。你也许会问:如果让整个页面都可以编辑,那么用户还怎么单击按钮,我们还怎么控制编辑过程呢?当然有办法,那就是把要编辑的文档放在一个<iframe>元素中,而这个元素就充当了一个超级的编辑框(见图18)。

这个页面中包含两个框,第一个是<iframe>。第二个是普通的<div>,其中显示编辑后的启示录页面的HTML标记。页面中的两个按钮控制着显示,用于将<iframe>切换为设计模式

这个页面的标记十分简单。以下就是这个页面<body>元素中的所有内容:

<h1>Editable Page</h1>
<iframe id="pageEditor" src="ApocalypsePage_Revised.html"></iframe>
<div>
  <button onclick="startEdit()">Start Editing</button>
  <button onclick="stopEdit()">Stop Editing</button>
</div>

<h1>Edited HTML</h1>
<div id="editedHTML"></div>

显然,这个例子有赖于startEdit()和stopEdit()方法,这两个方法与前面的示例类似。只不过这里的代码修改的是designMode属性,而非contentEditable属性:

function startEdit() {
  //把<iframe>转换为设计模式
  var editor = document.getElementById("pageEditor");
  editor.contentWindow.document.designMode = "on";
}

function stopEdit() {
  //关闭<iframe>的设计模式
  var editor = document.getElementById("pageEditor");
  editor.contentWindow.document.designMode = "off";

  //显示编码后的HTML(仅为验证确实可以编辑)
  var htmlDisplay = document.getElementById("editedHTML");
  htmlDisplay.textContent = editor.contentWindow.document.body.innerHTML;
}

通过这个例子可以更好地体验富文本编辑功能。例如,单击图片就可以在浏览器中操作它。可以调整它的大小,把它拖到新位置,或者单击之后按删除键就可以把它删除。如果页面中有表单控件,那么对它们也可以执行相同的操作。

参考文档