第七章 Form API
本章我们将讨论一种旧貌换新颜的技术——HTML表单。HTML表单自问世以来,一值是Web的核心技术之一。如果没有表单,那么很多应用,如在钱交易、论坛以及搜索等,根本无法实现。
尽管HTML5 Forms已经出现多年,也无论从规范还是从具体实现来说,它始终都是最不稳定的一块内容,有好消息也有坏消息,好消息是这部分的发展速度非常快,坏消息是开发人员需要仔细选择新的表单控件,以确保兼容所宿浏览器。HTML5 Forms规范详细规定了大量的表单API,新发布的兼容HTML5的主流浏览器都会或多或少地添加对表单控件的支持,这其中不乏一些有用的验证功能。
我们将利用一章的篇幅梳理表单控件,为你介绍哪些可以使用,哪些即将发布。
7.1 HTML5 Forms概述
如果熟悉HTML表单(对HTML高级编程感兴趣的人通常如此) ,那么你会很容易适应
HTML5 Forms的新增功能,如果不熟悉表单的基本知识,我们建议你先阅读一些表单创建和处理方面的教程。本意用到的旧表单知识在相关教程中都能找到,而作为开发人员的你一定乐于看到下面的几点:
- 表单仍然使用<form>元素作为容器,我们可以在其中设置基本的提交特性
- 当用户或开发人员提交页面时,表单仍然用于向服务器端发送表单中控件的值;
- 所有之前熟悉的表单控件,如文本框、单选按钮、复选框等,都沿用原来的使用方提(尽管增加了新的功能);
- 仍然可以使用脚本操作表单控件,喜欢自己修改和编写处理函数的开发人员大可放心。
7.1.1 HTML Forms与XForms
在过去的数年间(远在HTML5受到普遍关注以前),你可能听说过XForms的事。XForms是一个以XML为核心、功能强大却略显复杂的标准,它用于规范客户端表单的行为,而专门的W3C工作组研究这些行为已经近十年。XForms充分利用了XML Schema. 制订了针对验证和格式化的精确准则。不过,很遗憾,在没有安装插件的情况下,主流浏览器均不支持XForms。
7.1.2 功能性表单
HTML5表单规范更加注重对现有的简单HTML表单功能的改进,力求使之包含更多控件类型,同时它还着力解决Web开发人员今天面对的实际难题。不过,在开发过程中有一点需要牢记于心,那就是时刻关注表单的跨浏览器表现。
提示:一定要领会HTML5 Forms的核心设计理念:规范的核心是功能性动作和语义,而非外观和显示效果。
规范虽然详细描述了相关功能性PI的使用方法,如颜色、日历、数值选择器、邮件地址等,但是它并没有规定浏览器应该以何种方式将这些元素呈现给用户,这种做法带来了多方面的好处。首先,浏览器会竞相改善用户交互方式,其次,它分离了样式和语义, 再次,在未来或面对专用用户输入设备时,可以根据实际情况灵活调整交互方式。不过,除非目标浏览器平台能够支持Web应用中用到的所有表单控件,否则,你要向用户提供充足的上下文信息,以确保他们能够通过其他方式完成与应用间的交互。有了正确的提示和描述后,用户在使用过程中才不会产生疑问,即使浏览器不支持未知表单输入控件而采用了备选处理方案,用户也不会因此而受到影响。
HTML5表单包含了大量新的API 和元素类型,它们已经得到众多浏览器的支持。为了便于理解,我们将其分成两类:
- 新的输入型空间;
- 新的函数和特性。
在正式讨论之前,让我们了解一下各浏览器对HTML5 Forms的支持情况。
7.1.3 HTML5 Forms的浏览器支持情况
量然策容HTML5 Forms的浏览器在逐渐增多,但支持的程度还参差不齐。除了Opera浏览器之外,很多浏览器厂商还没有投入太多精力去支持新的表单控件。最近,可能是由于iPhone及其屏幕键盘显示的缘故,WebKit内核的浏览器添加了对HTML5Fonns的支持,这点随后我们还会详细讨论,诸如验证等高级功能也刚刚被引进,表7-1总结了HTML5 Forms的浏览器支持现状。
浏览器 | 支持情况 |
Chrome | 5.0-x支持的输入型控件有emil、number、tel、search和range。还支持大部分的验证功能 |
Firefox | 3.6版本不支持。4.0版本开始支持。不支持的新输入型控件有url、amail和range,这些控件会退化为普通文本输入框 |
Internet Explorer | 不支持。不过新的输入型控件会退化为普通文本输入框 |
Opera | 当前的多个版本完全支持最初的规范,包括验证功能 |
Safari | 4.0-x版支持的输入型控件有emil、number、tel、search和range。支持大部分验证功能。在Safari浏览器移动版中,有些类型的支持情况更好 |
对于新的HTML5Forms而言,检测浏览器支持情况其实意义不大, 因为根据新的设计原则,在旧的浏览器中新的表单控件会平滑降级。换言之,这意味使用HTML5 Forms的新元素完全没有后顾之忧,因为浏览器不支持新的输入型控件时,会把它们呈现为简单的文本输入框。不过,也正因为如此,多层(multi-tier)表单验证显得尤为重要(随后我们会详细讨论),即使现代浏览器都支持对表单控件的数据类型进行验证,我们也不能完全依赖其自身提供的验证器。
现在,我们已经了解了浏览器的支持情况。接下来,让我们逐一了解HTML5规范中新增的表单控件。
7.1.4 输入型控件目录
从W3C网站上可以找到到HTML5中所有新增和修改的元素,具体的元素目录位于http://devw3.org/html5/markup/。
目录列出了HTML页面中当前已有以及未来可能发布的所有元素。新增的和修改过的元素也在目录清单中。然而,列表中所谓的”新”是相对于HTML4规范而言一一并不代表该元素已经获得浏览器支持或者出现在了最终规范中。明确了这一点后,让我们开始讨论HTML5引人的新的表单元素。表7-2列出了typy特性对应的新的特性值。Web开发人员都会非常熟悉<inputtype=”text”>和<input type= ” chekcbox “>。 新特性值的使用方式与现有的模式类似。
类型 | 用途 |
tel | 电话号码 |
电子邮件地址文本框 | |
url | 网页的URL |
search | 用于搜索引擎,比如在站点顶部显示的搜索框 |
range | 特定值范围内的数值选择器,典型的现实方式是滑动条 |
新的输入型控件有什么用呢?用于API编程吗?貌似没多大用处。事实上,就tel 、email、url和search来说,我们无法通过合适的特性值把它们与简单输入类型text区分开来。
将输入控件指定为特殊的类型会得到什么呢?能够得到专用的输入型控件。(适用范围可能有限,而且很多桌面浏览器可能也不支持。)
以email为例:
<input type=”email”>
这与以往的使用方式不同,以前只是声明单纯的text类型:
<input type=”text”>
这样做的好处是,可以让浏览器在支持控件的情况下,为用户呈现不同的输入方式或用户界面,而且浏览器还可以在表单提交以前对其做进一步的验证,我们会在本章后面讨论验证。
移动设备浏览器向来是最先支持这些表单控件类型的浏览器。通过识别不同的类型,浏览器会显示不同的输入界面。当类型为text时,iPhone 手机会显示标准的屏幕键盘,如图7-1所示。
当类型为emaìl时,iPhone 手机会改变屏幕键盘,显示@符号,图7-2所示。
图7-1 类型为text时代屏幕键盘
图7-2 类型为email时代屏幕键盘
请注意二者在空格键区域的细微差别,类型为email时,在空格键区插入了@和.符号以便于输入电子邮件地址。url和search类型与此类似,键盘上会有局部微调。不过,桌面版的Safari浏览器,以及其他没有明确支持email 、url、search和tel类型的浏览器,只会显示普通文本输入框。未来的浏览器,即使是桌面版本,也可能会针对不同类型的输入框向用户提供直观的提示。以Opera浏览器为例,它会在输入框旁边显示一个小信封图标,表明应该在此输入框中输入电子邮件地址。尽管目前浏览器的支持情况有限,我们还是可以在Web应用程序中使用这些类型,因为浏览器会针对这些类型进行优化,或者干脆将其降级为普通文本输入框。
还有一种逐渐得到浏览器青睐的类型一<input type=”range “>。设计range类型的目的是为了让用户在指定的数值范围中进行选择。例如,在表单中使用range输入型控件来限制年龄选择的范围(如限定最小年龄为18)。通过创建range控件并为其指定min和max值,开发人员能够在页面上显示出一个数值范国选择器,用户只能选择指定范围内的数值。以Opera浏览器为例,如下代码:
<input type=”range” min=”18” max=”120”>
会生成一种便捷的控件,让用户从受限的年龄范围内选择合适的值。在最新的Opera浏览器中,其显示效果如下:
有点遗憾, 浏览器没有在滑动条上显示划度对应的数值大小。这样,用户也就无从得知自己选择的数值。解决这个问题很简单,添加onchange处理函数以便显示区域能够基于range控件值的改变而改变,具体实现见代码清单7-1。
代码清单7-1 用于更新数值显示的onchage处理函数
<script type="text/javascript"> function showValue(newVal) { document.getElementById("ageDisplay").innerHTML = newVal; } </script> <label for="age">Age</label> <input id="age" type="range" min="18" max="120" value="18" onchange="showValue(this.value)"> <span id="ageDisplay">18</span>
显示效果好多了,如下图所示。
Opera和基于WebKit 的浏览器(Safari和Cbrome)现已支持range 类型的input元素。 Firefox浏览器有对range类型的支持计划,但在本书写作过程中还未见进展。在Firefox浏览器中使用range类型时,目前只会显示常规的文本输入框。
HERE BE DRAGONS
“有句俚语叫‘ Here be dragons’,历史土人们会用它在地图上标出存在永知潜在危险的区域。表7-3所示的表单元素亦是如此。虽然规范描述得很清晰,而且它们也已经存在一段时间了,但其中的大多数元素仍然缺乏在实践中的应用。
期待有一天,浏览器开发者可以改造设计、弥合缺憾并响应反馈意见。届时,表7-3所示的表单元素会发生翻天覆地的变化。与其在开发中应用这些元素,倒不如将它们看做是HTML5表单发展的风向标。如果你坚持现在就用他们,那么后果自负…………“
——Brian
还有一些已列入规范但尚未得到广泛支持的表单元素,见表7-3。
类型 | 用途 |
number | 只能包含数值的文本框 |
color | 颜色选择器,基于调色盘或者取色版进行选择 |
datetime | 显示完整的日期和时间,包括时区如图7-3所示 |
datetime-local | 显示日期和时间弥补含时区 |
time | 不含时区的时间选择器和指示器 |
date | 日期选择器 |
week | 某年终的周选择器 |
month | 某年终的月选择器 |
尽管一些先进的浏览器已经初步支持这些元素(例如图7-3中是Opera的datetime 元素的显示效果),不过,本章不会把重点放在它们身上,因为这些元素有可能发生巨大的变化。请继续关注未来的版本吧!
Figure 7-3. Display for an input of type datetime
7.2 使用HTML5 Forms API
熟悉了新的表单元素类型之后,接下来我们介绍新旧表单控件中都有的特性和API。开发功能强大的Web应用程序的用户界面时,代码量通常会很大。而表单控件的很多特性和API旨在有效减少代码量。使用某些新特性,你甚至可以开发出前所未见的界面效果,即使在最差的情况下,也可以省去页面中大量的脚本代码。
7.2.1 新的表单特性和函数
我们先来讨论HTML5 新增的特性、函数和元素。如同新增的输入型控件一样,不管目标浏览器是否支持新增特性,我们都可以放心使用它们。因为市面上的浏览器在不支持这些特性时,会直接忽略它们,而不是报错。
1、placeholder
当用户还没有输入值的时候,输入型控件可以通过Placeho1der、特性向用户显示描述性说明或者提示信息。这在目前流行的用户界面框架中很常见,而主流JavaScript框架也提供了类似的功能。不过,有了placeholder特性以后,现代浏览器就可以内量这项功能了。
使用placeholde特性只需将说明性文字作为该特性值即可。除了普通的文本输入框外,
Email、number、url 等其他类型的输入框也都支持placeholder特性。
<label>Runner:<input name=”name”placeholder=”first and last name “required></ladel>
在谷歌Chrome等支持placeholder特性的浏览器中,特性值会以浅灰色的样式显示在输入框中,当页面焦点切换到输入框中,或者输入框中有值了以后,读提示信息就会消失。
在不支持placeholder的浏览器中运行时,此特性会被忽略,以输入型控件的默认方式显示。
类似地埋在输入值的时候,placeholder文本也不会出现。
早在Intemet Explorer 5.5就已经引入的autocomP1ete特性如今终于标准化了。万岁!(尽管浏览器从它出现时就一直提供支持,但规范化的行为对开发人员都有好处。)
浏览器通过autocoplete特性能够知晓是否应该保存输入值以备将来使用。例如不保存的代码如:
<input type=”text“name=”creditcard”autocomplete=”off”>
Autocomplete特性应该用来保护敏感用户数据,避免本地浏览器对他们进行不安全地储存。
表7-4显示了autocomplete的不同行为。
类型 | 作用 |
On | 该字段无需受到保护,值可以被保存和恢复 |
Off | 该字段无需受到保护,值不可以被保存 |
unspecified | 包含<form>的默认设置。如果没有被包含在表单中或没有指定值,则行为与设置为on时相同 |
3、autofocus
页面载入时,开发人员通过autofocus特性可以指定某个表单元素获得输入焦点。每个页面上只允许出现一个autofocus特性。如果设置了多个autofocus特性,则相当于未指定此行为。
提示:如果页面内容的呈现依赖于门户页面或者共享内容页面,那么狠难做到每个页面只有一个autofocus控件。所以,如果你无法完全控制整个页面,就不要指望基于autofocus特性获取焦点。
为了让控件(如搜索文本输入框)自动获取焦点,只需在元素中设置autofocus特性即可:
<input type=”search”name=”criteria”autofocus>
与其他布尔类型特性一样,使autofocus特性生效不需要专门将其值设为true.
提示在用户不希望焦点转移的情况下使用autofocus会惹恼用户。很多用户习惯与用键盘导航,这时切换表单控件的焦点会适得其反。最佳方案是仅当表单取全部默认值的情况下,才使用autofocus特性。
4、list特性和datalist元素
通过组合使用list特性和datalist元素,卡法人员能够为某个输入型控件构造一张选值列表,其使用方法如下。
(1)创建id特性值唯一的datalist元素,该元素可插入文档的任意位置。
(2)添加若干option元素作为datalist匀速,他们表示某控件推荐选值的全集。例如,datalist元素若表示电子邮件列表,那么每个备选的e-mail地址都应该放入option元素中,代码如下:
<datalist id="contactList"> <option value="x@example.com" label="Racer X"> <option value="peter@example.com" label="Peter"> </datalist>
(3)将input元素的list特性值设为datalist元素的id值,可以实现二者的关联。
<input type=”email”id=”contacts” list=”contactlist”>
在支持list特性和datalist元素的浏览器(如opera)中,自定义列表控件显示效果如下:
5、Min和Max
在之前的<input type=”range”>示例中我们看到,通过设置Min和Max特性,可以将range输入框的数值输入范围限定在最低值和最高值之间。着两个特性既可以只设置一个,也可以两个都设置,还可以都不设置,输入型控件会根据设置的参数对值范围作出相应调整。例如,创建一个表示信心大小的range控件,值范围从0%至100%,代码如下:
<input id=”confidence”name=”level”type=”range”min =”0”max=”100”value=”0”>
上述代码会创建一个最小值为0、最大值为100的range控件,无巧不成书,默认值恰好也是0和100。
6. Step
对于输入型控件,设置其step特性能够指定输入值递增或递减的粒度。例如,按一下方式将表示信心大小的range控件的step特性设置为5:
<input id=”confidence” name=”level” type=”range” min=”0” max=”100” step=”5” value=”0”>
设置完成后,控件可接受的输入值只能是初始值与5的倍数之和。换句话说,只能输入0、5、10……….100,至于是由输入框还是滑动输入则由浏览器决定。
Step特性的默认值取决于控件的类型。对于range控件,step默认值为1.为了配合step特性,HTML5引入了stepUP和stepDown两个函数对其进行控件。
不出所料,两个函数的作用分别是根据step特性的值来增加或减少控件的值。如此一来,用户不必输入就能够调整输入型控件的值了。
7、valueAsNumber函数
valueAsNumber函数的作用是完成控件值类型在文本与数值贱的相互转换。他即使getter函数有事setter函数。作为getter函数调用时,valueAsNumber函数将文本类型转换成number类型,以方便计算。如果转换失败,则会返回NAN值(Not-a-Number)。
valueAsNumber也可用于为输入型空间设置number值。例如,上述表示信心大小的range控件可以这样是指:
Document.getelementbyid(“confidence”).valueAsNumber(65);
输入的数值必须同时满足min、Max和step要求,否则会抛出错误。
8、Required
一旦为某输入型设置了required特性,那么比项必填,否则无法提交表单。以文本输入框为例,要将其设置为必填项,按照如下方式添加required特性即可:
<input type=”text” id=”firstname” name=”first” required />
如果此文本框中没有值,则无论以编程方式还是用户操作都将无法提交表单。Required特性是最简单的一种表单验证方式,不过表单雁阵功能远不止于此。接下来,我们就详细的讨论。
7.2.2 表单验证
在深入探讨表单验证之前,然给我们先思考一下表单验证的真实含义。就其核心而言,表单验证时一套系统,他为终端用户检测无效的控件数据并标记这些错误。换言之,表单验证就是在表单提交如武器前对其进行一系列的检测并通知用户纠正错误。
但是真正的表单验证时什么?
是一种优化。
之所以说表单验证是一种优化,是因为仅通过表单验证机制不足以保证提交给服务器的表单数据时正确和有效的。另一方面面设计表单验证时为了让web应用更快地抛出错误。换句话说,最好利用浏览器内置的处理机制来告知用户网页内包含无效的表单控件值。过去,数据在网络上转一圈,仅仅是为了让服务器通知用户他输入了错误的数据。如果浏览器完全有能力让错误在离开客户端之前就被捕获到,那门我们应该利用这个优势。
不过,浏览器的表单检测还不足以处理所以的错误。
恶意还是误解
“尽管HTML5规范大幅提升了浏览器内置表单检测方面的能力,但是任然不能完全取代服务器端验证。或许永远都不可能。
显然,许多错误条件是需要与服务器端交互才能验证,比如信用卡是否被授权,甚至是基本的验证。然而,及时是普通验证也不能只依赖客户端。用户使用的浏览器可能本身就不支持表单验证。少数用户屏蔽脚本功能,导致基于特性值的基本验证外的验证功能无法使用。还有一些用户会利用诸如Firefox greasemonkey等插件修改页面内容,进而导致表单验证功能失效。归根结底,重要数据的验证不能仅依靠客户端验证一种方式。重要数据存储在客户端,他就可以被篡改。
HTML5的表单验证可以让用户快速获得重要反馈,但正确性方面绝对不应该 依赖于它!”
——Brian
话虽如此,HTML5还是引入了八种用于验证表单控件的数据正确性的方法。然给我们依次了解一下,不过先要介绍一下用于反馈验证状态的validitystate对象。
在支持HTML5表单验证的浏览器中,可以通过表单控件来访问validitystate对象:
var valcheck=document.myform.myinput.validity;
这个代码获取了名为myinput的表单元素的validitystate对象。对象包含了对所有八种验证状态的引用,以及最终验证结果。
调用方式如下:
Valcheck.valid
执行文笔,我们会得到一个布尔值,他表示表单控件是否已铜鼓了所有的验证约束条件。可以吧valid特性看做是最终验证结果:如果所有八个约束条件都通过了,valid特性就是true。否则,只要有一项约束没有通过,valid表示都是false
提示: validitystate对象时一个实时更新的对象。获得某表单元素的validitystate对象 后, 当表单元素内容发生变化时,你可以通过它来获得更新后的检测结果。
如前所述,任何表单元素都有八个可能的验证约束条件。每个条件在validitystate对象中都有对应的特性名,可以用适当的方式访问。让我们逐一分析,看看他们是如何与表单控件关联的,以及如何基于validitystate对象来对他们进行检查:
Validitystate
目的:确保表单控件中的值已填写。
用法:在表单控件中将required特性设置为TRUE
示例:
<input type=”text”name=”mytext”required>
详细说明:如果表单控件设置了required特性,那么在用户填写或者通过代码调用方式填值之前,控件会一直处于无效状态。例如,空的文本输入框无法通过必填检查,除非在其中输入任意文本。输入值为空时,valuemissing会返回TRUE。
Patternmismatch
目的:根据表单控件上设置的格式规则验证输入是否为有效格式。
用法:在表单控件上设置pattern特性,并赋予实单观点匹配规则。
示例:
<input type=”text” name=”creditcardnumber” pattern=”[0-9]{16}” title=”A credit card number is 16 digits with no spaces or dashes”>
详细说明:pattern特性向开发人员提供了一种强大而灵活的方式来为表单的控件值设定正则表达式验证机制。当为控件设置了pattern特性后,只要输入控件的值不符合模式规则,patternmismatch就会返回TRUE值。从引导用户和技术参考两方面考虑,你应该在包含pattern特性的表单控件中设置title特性以说明规则的作用。
Toolong
目的:避免输入值包含过多字符。
用法:在表单控件上设置Maxlength特性。
示例:
<input type=”text”name=”limitedtext”maxlength=”140”>
详细说明:如果输入值的长度超过了Maxlength,toolong特性会返回true。虽然表单控件通常会在用户输入时限制最大长度,但在有些情况下,如果过程序设置,还是会超出最大值。
rangeUnderflow
目的:限制数值型控件的最小值。
用法:为表单控件上设置min特性,并赋予允许的最小值。
示例:
<input type=”range”name=”agecheck”min=”18”>
详细说明:在需要做数值范围检测的表单控件中,数值很可能会暂时低于设置的下限。此时,validitystate的rangeunderflow特性将返回true。
Rangeoverflow
目的:限制数值型控件的最大值。
用法:为表单控件设置Max特性,并赋予允许的最大值。
示例:
<input type=”range”name=”kidagecheck”max=”12”>
详细说明:与rangeunderflow类似,如果一个表单控件的值比Max更大,特性将返回true。
Stepmismatch
目的:确保输入值符合min、Max及step设置。
用法:为表单控件设置step特性,指定数值的增量。
示例:
<input type=”range”name=”confidencelevel”min=”0”max=”100”step=”5”>
详细说明:此约束条件用来保证数值符合min、Max和step的要求。换句话说,当前值必须是最小值与step特性值的倍数之和。例如,范围从0到100,step特性值为5、此时就不允许出现17,否则stepMismatch返回true值。
Customerror
目的:处理应用代码明确设置及计算产生的错误。
用法:调用setcustomvalidity(message)将表单控件置于customerror状态。
示例:
passwordConfirmationField.setCustomValidity("Password values do not match.");
详细说明:浏览器内置的验证机制不适用时,需要显示自定义验证错误信息。当输入值不符合语义规则时,应用程序代码应设置这些自定义验证消息。
自定义验证消息的典型用例是验证控件中的值是否一致。例如,密码和密码确认两个输入框的值不匹配(在”进阶功能“一节我们将深入研究此例)。只要定制了验证消息,控件就会处于无效状态,并且customerror返回true。要清除错误,只需在控件上调用setcustomvalidity(”“)即可。
其他用于验证的特性及函数
总之,着八个约束可以让卡法人员找到表单验证失败的确切原因。或者,如果你不关心失败的具体原因,那么只访问validitystate对象的valid布尔值即可,他代表了验证的最终结果。如果所有八个约束条件都返回false,那么valid特性将返回true。此外,在编写验证代码时,表单控件还支持其他一些有用的特性和函数。
Willvalidate特性
Willvalidate特性仅用来说明某表单控件是否将进行验证。如果required特性。Pattern特性等设置在了控件上,那么通过willvalidate特性,你可以得知验证将会执行。
Checkvalidity函数
即使没有用户输入,也可以使用checkvalidity函数对表单验证。一般情况下,表单验证发生在用户或者脚本提交表单时,checkvalidity函数却能在任何时间对表单进行验证。
提示 在表单控件上调用checkvalidity函数不仅会进行验证,还会触发所有结果时间和UI触发器,就好像表单已经提交了一样。
validationMessage特性
撰写此书的时候还没有一个浏览器支持validationMessage特性,不过在你阅读本书的时候,很可能已经有了。validationMessage特性允许你通过编程方式查询本地化错误信息,浏览器基于当前验证状态显示的也是这些信息。例如,如果required特性没有值,则浏览器可能会提示用户:”这是必填字段“(this field required a value)。如果浏览器支持validationMessage特性,这段提示信息会由validationMessage返回并依据控件当前验证状态进行调整。
7.2.3 验证反馈
关于验证反馈,有一个到目前为止我们一直回避的问题——浏览器怎样以及何时将验证错误信息反馈给用户呢?规范中没有规定用户界面如何展示错误信息,而且目前各浏览器的展现方式也不尽相同。以opera为例,在opera10.5中,浏览器会通过弹出提示消息并将字段置红的方式来标记产生错误的字段。
相比之下,谷歌chrome浏览器只会在发现错误时将焦点定位到出问题的控件,哪种做法正确呢?
二者均非标准。不过,如果开发人员想控制反馈给用户的错误消息,可以使用一个合适的处理函数:invalid事件。
只要发生表单验证(不管是在提交表单时,还是直接调用checkvalidity函数),所有未通过验证的表单都会接收到一个invalid事件。Invalid事件可以被忽略、观察、甚至取消。可以为接收此通知的字段添加invalid事件处理函数。贱代码清单7-2:
代码清单7-2 为invalid事件添加处理函数
// event handler for "invalid" events function invalidHandler(evt) { var validity = evt.srcElement.validity; // check the validity to see if a particular constraint failed if (validity.valueMissing) { // present a UI to the user indicating that the field is missing a value } // perhaps check additional constraints here… // If you do not want the browser to provide default validation feedback, // cancel the event as shown here evt.preventDefault(); } // register an event listener for "invalid" events myField.addEventListener("invalid", invalidHandler, false);
现在让我们分析一下上面的代码。
首先,声明了一个事件处理函数来接收invalid事件。在该函数内,首先要做的事检测事件来源。之前我们已经了解到,表单控件验证错误触发了invalid事件。因此,事件的srcelement应该是发生错误的表单控件。
从事件来源中,我们得到了validity对象。使用validitystate实例,通过检测各个约束条件,确定错误的出处。在示例中,因为我们知道字段有required特性。所以首先要检测是否满足valueMissing约束条件。
如果验证成功,我们可以修改用户界面,提示用户在必填字段中输入值。开发人员可以决定出错信息的显示方式——置于弹出警告框中或者显示在页面特定区域上。
在我们告知用户出了什么错,如何改正后,接下来要决定是否需要浏览器显示内置的反馈信息。默认情况下,浏览器会自动显示内置的反馈信息。为了阻止浏览器显示默认的错误信息,我们调用了evt.preventDefault()以阻止浏览器的默认行为,并自行处理错误信息的显示。
从这份代码中我们再次看到,开发人员的选择余地很大。HTML5 Forms API 让你的开发更具灵活性,既可以使用定制的API,又可以使用默认的浏览器行为。
关闭验证
不管验证API背后的功能有多么强大,但还是有一些原因,促使你关闭对某个控件或者整个表单的验证,最常见的情况是暂存袭单内容或者供将来查阅,这时,即使内容无法通过验证也没关系。
想象一下这样的场景,用户进入了一个复杂的订单录入表单,填写到一半时忽然有急事需要处理,理想的情况下,你可能会向用户提供一个”保存”按钮,点击后将已填的信息提交至服务器保存。但如果表单没有全部完成,验证规则就会阻止内容的提交。用户会非常失望,因为他必须选择要么继续完成表单,要么放弃已经填完的部分。
为了解决类似问题,表单本身可以通过代码方式设置noValidate特性。这样一来,所有的验证逻辑都会被放弃,只会单纯地提交表单。这个特性可以通过脚本设置。也可以直接标记。
关闭验证更好的办法是在如表单提交按钮(type特性值为submit)这样的控件上设置
formNoValidate特性。下面的示例在名为”save” 的提交按钮上设置formNoVa1idate特性:
<input type=”submit”formnovalidate name=”save”value=”save current progress”> <input type=”submit”name=”process”value=”process order”>
乍一看,代码创建了两个普通的提交按钮。第二个按钮会正常提交表单。但是,第一个按钮设置noValidate特性,所以点击该按钮会绕过所有的验证。换句话说,在数据被提交到服务器之前,不会再对其进行正确性检查了。当然,服务器端还需要做一些相应的设置以处理未经验证的数据,但最佳做站是任何时候都应做这些设置。
7.3 构建HTML5 Forms应用
现在,让我们利用本章描述的工具,创建一个简单的注册页面,以此来演示HTML5 Forms的新特性。再回到我们所熟悉的跑步俱乐部应用,我们将为这家俱乐部创建一个比赛注册页面,其中会包含本章介绍的新的表单元素和验证功能。
一如既往,示例文件的源代码位于code/forms文件夹。我们无需花费太多精力关注css和次要的标记,重点关注页丽的核心部分即可。照例,我们先看看图7-4所示的最终页面,然后将其拆成一系列片段分别讨论。
图7-4 比赛注册表单页面示例
上面的注册页面包含了本章所探讨的众多元素和API,包括验证功能。虽然不同的浏览器会导致页面实际显示效果上的差异,但是在浏览器不支持某个功能时。页面会平滑退化。
现在来看看代码。
在之前的示例中,我们已经讨论过页眉、导航、页脚。现在,这个页面包含了一个<form>元素。
<form name="register"> <p><label for="runnername">Runner:</label> <input id="runnername" name="runnername" type="text" placeholder="First and last name" required></p> <p><label for="phone">Tel #:</label> <input id="phone" name="phone" type="tel" placeholder="(xxx) xxx-xxx"></p> <p><label for="emailaddress">E-mail:</label> <input id="emailaddress" name="emailaddress" type="email" placeholder="For confirmation only"></p> <p><label for="dob">DOB:</label> <input id="dob" name="dob" type="date" placeholder="MM/DD/YYYY"></p>
在这一部分中,我们可以看到四个主要的input标签:姓名(name)、电话(phone) 、电子邮件(Email}和生日(birthday)。对于每个input标签:我们都为其设定了一个带描述信息的<label>,并通过for特性将它们绑定到了实际控件上。我们还设置了placeholder特性以向用户示范应该填写什么样的内容。
对于姓名文本输入框,我们为其设定了required特性来使其成为必镇项。如果未输入任何值,将会触发表单验证的valueMissing约束条件。在电话文本输入框中,我们将其声明为tel类型。有些浏览器支持tel类型,有些则不支持,还有些浏览器能够提供优化过的键盘?。
同样,电子邮件文本输入框标记记为email类型。处理方式取决于浏览器版本。若输入值不是一个有效的email地址,则某些浏览器会抛出typeMísmatch约束异常。
最后,生日文本输入框被声明成了date类型。现在还没有多少浏览器支持这一特性,但将来一旦支持的话,浏览器会自动呈现一个日期选择控件供用户使用。
<fieldset> <legend>T-shirt Size: </legend> <p><input id="small" type="radio" name="tshirt" value="small"> <label for="small">Small</label></p> <p><input id="medium" type="radio" name="tshirt" value="medium"> <label for="medium">Medium</label></p> <p><input id="large" type="radio" name="tshirt" value="large"> <label for="large">Large</label></p> <p><label for="style">Shirt style:</label> <input id="style" name="style" type="text" list="stylelist" title="Years of participation"></p> <datalist id="stylelist"> <option value="White" label="1st Year"> <option value="Gray" label="2nd - 4th Year"> <option value="Navy" label="Veteran (5+ Years)"> </datalist> </fieldset>
在上面的代码段中,我们设置了一些控件,用于选择T-shirt。第一组控件是标准的单选按钮,用于选择T-shirt尺寸。
随后的代码更为有趣。我们用到list特性及其关联的<datalist>元素。在<datalist>中,我们声明了一系列用于列表显示的T-shirt类型,并使用了不同的value特性和label
特性来区分它们,不同的T-shirt类型代表了不同的参赛年龄段。尽管这个列表非常简单,但其中的技术同样适用于列表长度动态变化的情况。
<fieldset> <legend>Expectations:</legend> <p> <label for="confidence">Confidence:</label> <input id="confidence" name="level" type="range" onchange="setConfidence(this.value)" min="0" max="100" step="5" value="0"> <span id="confidenceDisplay">0%</span></p> <p><label for="notes">Notes:</label> <textarea id="notes" name="notes" maxLength="140"></textarea></p> </fieldset>
最后一段代码中,我们为用户创建了一个滑动条,来表示他对在比赛中取得名次的信心。为此,我们使用了range类型的输入框。示例中信心以百分比的方式加以衡量,所以我们在输入控件上设置了min 、max 和step特性,这样,用户的信心比例就会被强制约束在正常范围内。此外,我们将信心比例变化的步长设为5%。如果浏览器支持range特性并以滑动条形式展现,那么你可以很直观地看到其效果。尽管简单的控件交互不太可能触发它们。但控件上仍有一些验证约束,如rangeUnderflow、rangeOverflow 和stepMismatch。
因为range控件默认无法显示其所代表的数值,所以我们需要为应用添加此功能。range控件的onchange处理程序会操纵id为confidenceDisplay的span标签以显示数值,不过,想看效果还要再等一会?。
在这段代码的最后,我们添加了一个<textarea>标签,用于填写注册中的附加信息,通过在textarea元素上设置maxLength 特性,我们为textarea元素添加了tooLong 约束条件检测,因为你无法保证用户不会将一大段文本粘贴到textarea元素中。
<p><input type=”submit”name=”register”value=”register”></p> </form>
最后,添加一个用以提交注册表单的按钮来完成整个控件部分的编写。示例中,注册信息实际上不会发往任何服务器。
还差一些脚本代码没有编写:如何更新信心滑动条的显示,以及如何覆盖浏览器内置的表单验证反馈。尽管浏览器默认的表单异常处理机制能够满足开发的要求,但了解其他选择也是有必要的。
<script type="text/javascript"> function setConfidence(newVal) { document.getElementById("confidenceDisplay").innerHTML = newVal + '%'; } function invalidHandler(evt) { // find the label for this form control var label = evt.srcElement.parentElement.getElementsByTagName("label")[0]; // set the label's text color to red label.style.color = 'red'; // stop the event from propagating higher evt.stopPropagation(); // stop the browser's default handling of the validation error evt.preventDefault(); } function loadDemo() { // register an event handler on the form to // handle all invalid control notifications document.register.addEventListener("invalid", invalidHandler, true); } window.addEventListener("load", loadDemo, false); </script>
上面这段代码包含下面几部分。首先是setConfidence()函数,用于处理range控件的change事件。每次滑动条值发生变化,都会找到id为confidenceDi splay的<span>并更新相应文本。从示例代码中可以看到,配置非常简单。
提示:为什么range控件默认不显示数值呢?可能是为了让用户界面设计师能够定制精确的显示位置和显示外观。虽然把显示工作分离出来添加了工作量,但却更灵活了。
代码的最后展示了如何覆盖浏览器默认的验证异常处理。首先,注册事件监听器以监听invalid事件。为了在所有表单控件范围内捕获invalid事件,我们把处理函数注册在了表单上。再次提醒,一定要注册事件监听器,否则无法监听到invalid事件。
//在表单上注册事件处理函数,以处理所有未通过验证的控件通知 document.register.addEventListener(“invalid”,invalidHandler,true);
现在,只要表单元素触发了验证约束条件, ìnvalidHandler就会被调用。为了向用户提供比浏览器默认设置更加细致的反馈。我们将不合法的表单控件对应的label文本标为红色。实现这种效果先要通过表单控件的父节点找到相关的<label>标签。
//获取表单控件对应的label标签 Var label=evt.srcElement.parentElement.getElementsByTagName(“label”)[0] //将label字体颜色设置为red Label.style.color=”red”;
将label标签中的说明文本设置为红色后,我们要阻止浏览器或其他的invalId事件处理函数对invalid事件重复处理。利用DOM的强大功能,我们调用了preventDefault() 函数来停止浏览器默认的事件处理程序,并调用stopPropagation()阻止其他处理程序处理invalid事件。
//阻止事件冒泡 evt.stopPropagation(); //停止浏览器默认的验证错误处理函数 evt.preventDefault();
只要寥寥几步,我们就用定制的前端验证代码创建一个带有验证功能的表单!
7.4 进阶功能
尽管有些技巧我们在示例中用不上,但这并不妨碍他们在多种类型的HTML5 Web应用中发挥作用。本节,我们就来介绍一些简单实用的进阶功能。
密码是:Validation!
HTML5表单验证为常见的密码确认功能提供了一种便捷的实现方式。以往,标准的做法是在页面上提供两个密码输入框,只有当二者一致时方能提交表单。现在, 我们使用一种新方法,在表单提交之前,调用setCustomValidation 函数来确保两个密码辙λ框中的内容相匹配。
之前我们提到,当标准约束规则不适用时,CustomError可以用来处理表单控件中的错误。特别是当验证依赖于多个控件并行触发时,CustomError约束条件的应用优势更为明显,密码确认无疑是一个很好的示例。
ValidityState 对象引用被获取后,会一直处于激活状态,所以当密码字段不匹配时,应在ValidityState对象上设置自定义错误信息,而当密码字段相匹配时,立刻清除自定义错误信息。通过为密码字段添加onchange事件处理函数可以实现上述功能。
<form name="passwordChange"> <p><label for="password1">New Password:</label> <input type="password" id="password1" onchange="checkPasswords()"></p> <p><label for="password2">Confirm Password:</label> <input type="password" id="password2" onchange="checkPasswords()"></p> </form>
从上面的代码中可以看到,在包含两个密码字段的表单中,只要其中任何一个密码值发生变化,就会触发我们注册的事件。
function checkPasswords() { var pass1 = document.getElementById("password1"); var pass2 = document.getElementById("password2"); if (pass1.value != pass2.value) pass1.setCustomValidity("Your passwords do not match. Please recheck that your new password is entered identically in the two fields."); else pass1.setCustomValidity(""); }
这里提供了一种处理密码匹配的方法。取得两个密码字段的值,加以比较,如果它们不匹配,则抛出一个自定义错误。出于验证的目的,代码只需对其中一个密码字段设置错误信息。如果两个密码字段匹配,自定义错误信息会被空字符串所替换,这样就能够消除自定义错误。
在表单控件上设置错误信息后,你可以使用本章之前描述的方法向用户显示反馈信息,要求用户修改密码以使其符合匹配条件。
7.5 小结
本章,我们看到HTML5表单旧貌换新颜,这一切要归功于HTML5带来的新元素、新特性和新API。我们看到了一些新的输入型控件,以后还会陆续增多。我们还看到了如何直接在表单控件中集成客户端验证功能,以避免错误数据在客户端和服务器端来回传输。总之,在创建包含全面功能的Web应用界面方面。HTML5 Forms提供了多种能够有效降低脚本代码开发量的途径。下一章,我们将研究HTML5 Web Workers,在支持它的浏览器中,可以将长时间运行的任务放到多个独立的运行环境中去处理。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论