你的位置: 首页 畅游 HTML 5


疯狂的表单

 

开始潜入

E想必大家都知道网页表单为何物吧? 制作一个 <form>, 一些 <input type="text"> 元素, 也可能有一个 <input type="password">, 用 <input type="submit"> 按钮结束, 到此你完成了(表单).

你只是略知一二. HTML5对你在表单中可以使用得到的新的 input 类型做了详细的定义. 当我说 “使用,” 的时候,我的意思是你可以立即使用它们 — 不需要任何依赖,特殊技巧或者变通方法. 现在不要高兴得太早; 我并不是说所有这些激动人心的新特性在每个浏览器上都能得到实际上的支持. 噢,天啊,不, 我根本不是这个意思. 在现代浏览器中, 是的, 你的表单可以应付所有的难题. 但在传统浏览器中, 你的表单虽然仍能继续工作, 但是处理问题却没那么自如. 也就是说, 所有这些功能在每个浏览器上都会有一定的降级. 即使是IE6.

占位符文本

HTML5 给网页表单带来的第一项改进是能够给一个输入域(input field)设置 占位符文本. 只要输入字段是空的,或者没有得到焦点,输入栏里将一直显示占位符文本. 一旦你点击(或者切换)到输入栏, 占位符文本就会自动消失.

你可能之前有见过文本占位符. 比如, 现在的Mozilla火狐3.5的地址栏就包含了可以读取“搜索书签和历史记录”的占位符文本:

location bar with placeholder text

当你点击或者(TAB切换)到地址栏时, 占位符文本消失:

location bar with cursor and no placeholder text

讽刺的是, 火狐3.5并不支持添加占位符文本到您的自己的网页表单中. 这就是生活.

Placeholder 支持
IEFirefoxSafariChromeOperaiPhoneAndroid
·3.7+4.0+4.0+···

下面介绍如何将占位符文字包含到你的网页表单中:

<form>
  <input name="q" placeholder="Search Bookmarks and History">
  <input type="submit" value="Search">
</form>

不支持占位符(placeholder)属性的浏览器将会直接忽略它. 不会留下任何危害. 看看你的浏览器是否支持占位符文本.

咨询一下标记专家

问: 我可以在占位符属性中使用 HTML 标记吗? 我想插入一张图片, 或者想改变一下颜色.
答: 占位符(placeholder)属性只能包含文本, 不包含 HTML 标记. 不过, 有些浏览器中也有一些制造商定义的CSS扩展允许你改变占位符的样式.

Autofocus Fields

Autofocus support
IEFirefoxSafariChromeOperaiPhoneAndroid
··4.0+3.0+10.0+··

angry guy with arms up

Web sites can use JavaScript to focus the first input field of a web form automatically. For example, the home page of Google.com will autofocus the input box so you can type your search keywords. While this is convenient for most people, it can be annoying for power users or people with special needs. If you press the space bar expecting to scroll the page, the page will not scroll because the focus is already in a form input field. (It types a space in the field instead of scrolling.) If you focus a different input field while the page is still loading, the site’s autofocus script may “helpfully” move the focus back to the original input field, disrupting your flow and causing you to type in the wrong place.

Because the autofocusing is done with JavaScript, it can be tricky to handle all of these edge cases, and there is little recourse for people who don’t want a web page to “steal” the focus.

To solve this problem, HTML5 introduces an autofocus attribute on all web form controls. The autofocus attribute does exactly what it says on the tin: as soon as the page loads, it moves the input focus to a particular input field. But because it’s just markup instead of script, the behavior will be consistent across all web sites. Also, browser vendors (or extension authors) can offer users a way to disable the autofocusing behavior.

Here’s how you can set a form field to autofocus:

<form>
  <input name="q" autofocus>
  <input type="submit" value="Search">
</form>

Browsers that don’t support the autofocus attribute will simply ignore it. See whether your browser supports autofocus fields.

What’s that? You say you want your autofocus fields to work in all browsers, not just these fancy-pants HTML5 browsers? You can keep your current autofocus script. Just make two small changes:

  1. Add the autofocus attribute to your HTML markup
  2. Detect whether the browser supports the autofocus attribute, and only run your own autofocus script if the browser doesn’t support autofocus natively.

Autofocus with fallback

<form name="f">
  <input id="q" autofocus>
  <script>
    if (!("autofocus" in document.createElement("input"))) {
      document.getElementById("q").focus();
    }
  </script>
  <input type="submit" value="Go">
</form>
…

See an example of autofocus with fallback.

Setting focus as early as possible

Lots of web pages wait until window.onload fires to set focus. But the window.onload event doesn’t fire until after all your images have loaded. If your page has a lot of images, such a naive script could potentially re-focus the field after the user has started interacting with another part of your page. This is why power users hate autofocus scripts.

The example in the previous section placed the auto-focus script immediately after the form field that it references. This is the optimal solution, but it may offend your sensibilities to put a block of JavaScript code in the middle of your page. (Or, more mundanely, your back-end systems may just not be that flexible.) If you can’t insert a script in the middle of your page, you should set focus during a custom event like jQuery’s $(document).ready() instead of window.onload.

Autofocus with jQuery fallback

<head>
<script src=jquery.min.js></script>
<script>
  $(document).ready(function() {
    if (!("autofocus" in document.createElement("input"))) {
      $("#q").focus();
    }
  });
</script>
</head>
<body>
<form name="f">
  <input id="q" autofocus>
  <input type="submit" value="Go">
</form>

See an example of autofocus with jQuery fallback.

jQuery fires its custom ready event as soon as the page DOM is available — that is, it waits until the page text is loaded, but it doesn’t wait until all the images are loaded. This is not an optimal approach — if the page is unusually large or the network connection is unusually slow, a user could still start interacting with the page before your focus script executes. But it is still far better than waiting until the window.onload event fires.

If you are willing and able to insert a single script statement in your page markup, there is a middle ground that is less offensive than the first option and more optimal than the second. You can use jQuery’s custom events to define your own event, say autofocus_ready. Then you can trigger this event manually, immediately after the autofocus form field is available. Thanks to E. M. Sternberg for teaching me about this technique.

Autofocus with a custom event fallback

<head>
<script src=jquery.min.js></script>
<script>
  $(document).bind('autofocus_ready', function() {
    if (!("autofocus" in document.createElement("input"))) {
      $("#q").focus();
    }
  });
</script>
</head>
<body>
<form name="f">
  <input id="q" autofocus>
  <script>$(document).trigger('autofocus_ready');</script>
  <input type="submit" value="Go">
</form>

See an example of autofocus with a custom event fallback.

This is as optimal as the first approach; it will set focus to the form field as soon as technically possible, while the text of the page is still loading. But it transfers the bulk of your application logic (focusing the form field) out of the body of the page and into the head. This example relies on jQuery, but the concept of custom events is not unique to jQuery. Other JavaScript libraries like YUI and Dojo offer similar capabilities.

To sum up:

电子邮件地址

十几年来, 网页表单由极少种类的字段域组成. 最常见的类型由

域类型HTML 代码说明
checkbox<input type="checkbox">可以勾选或取消
radio button<input type="radio">能与其它inputs元素成组
password field<input type="password">键入的字符以星号显示
drop-down lists<select><option>…
file picker<input type="file">弹出一个“打开文件“的对话框
submit button<input type="submit">
plain text<input type="text"> type 属性可以缺省

所有的这些输入类型在 HTML5中仍将有效. 如果你“升级到 HTML5” (有可能通过 改变你的 DOCTYPE), 你不需对你的网页表单作出任何改变. 向后兼容万岁!

然而, HTML5 定义了几个新的字段域类型, 没有理由去使用它们, 对于这一点原因会变得很清晰.

EMAIL地址是这些新的输入域类型中的第一个. 它是这样的:

<form>
  <input type="email">
  <input type="submit" value="Go">
</form>

我想写一个以 “在不支持type="email"的浏览器中…” 为开头的句子, 但是却停了下来. 为什么呢? 因为我不确定如果说某个浏览器不支持type="email"意味着什么. 所有的浏览器都“支持” type="email". 它们可能不会对它作出任何特殊处理(随后你将会看到一些有关特殊处理的例子), 但是无法识别 type="email" 的浏览器将把它当作 type="text" 处理, 并将其渲染成纯文本域.

我不能一味地强调这有多重要. 有数百万的网页表单要求你输入电子邮件地址, 它们都使用 <input type="text">. 当你看见一个文本框, 在其中键入你的邮件地址, 仅此而已. 随之而来的HTML5定义了 type="email". 是不是浏览器都很怪异呢? 不. 地球上的每一个浏览器都是把未知的 type 属性当做 type="text" 来处理的— 即使IE6也是如此. 因此你可以立刻使用type="email"来 “升级” 你的网页表单.

如果说浏览器的 DID 支持 type="email" 那意味着什么呢? 的意义可多着呢. HTML5 规范并不要求新的输入类型有任何特定的用户界面. Opera 使用一个小的email图标来呈现该表单字段. 其它的 HTML5 浏览器 ,如 Safari 和 Chrome 仅简单地将其渲染成文本框 — 完全像 type="text" — 这样用户永远都不会觉察到其中的不同 (除非他们查看源代码).

接下来是 iPhone.

iPhone 没有物理键盘. 所有的“键入”都是通过轻按适时弹出的屏幕键盘完成的, 就像网页上的表单域获得焦点一样. 苹果公司在 iPhone 的网页浏览器中做了一些非常聪明的事情. 它可以识别一些新的 HTML5 输入类型, 并且在屏幕键盘上做出动态的改变为其优化.

比如, 电子邮件地址是文本, 对吗? 当然, 但是他们是特殊类型的文本. 例如, 事实上所有的邮件地址都包含@符号以及至少一个句点(.), 但是他们不会包含任何空格. 以当你使用iPhone并聚焦在一个 <input type="email"> 元素上时, 你的面前就会出现一个空格键比平时更小的屏幕键盘, 该键盘上还含有为@.字符加上的专属键.

iPhone rendering input type=email field
Test type="email" for yourself.

总之: 现在尚没有立即将你的邮件地址表单域转换到 type="email" 的趋势. 事实上没有人会注意到这一点, 除了iPhone用户, 可能连iPhone用户也不会注意到. 但是意识到这一点的人将会静静一笑并感谢你使他的网页体验更容易了一些.

网页地址

网页地址 — 众所周知的 URLs, 在 在学术上 也被称为上也被称为 URIs — 是另一种类型的专门文本. 网页地址的语法受到互联网相关标准的约束. 如果某人要求你在表单中键入网页地址, 他们期望你输入的是如 “http://www.google.com/”这样的网址, 而不是 “法伍德路125号.” 正斜杠是常见的 — 连 Google 的首页都有三个. 句点也是常见的, 但是空格却不允许使用. 并且每个网页地址都有一个域名后缀, 如 “.com” 或 “.org”.

请注意... (大家鼓掌)... <input type="url">. 在 iPhone 上, 它是这样的:

iPhone rendering input type=url field
Test type="url" for yourself.

iPhone 改变了它的虚拟键盘, 就像它对电子邮件地址所做的一样 , 只是现在是为网页地址做优化. T空格键完全被三个虚拟键代替: 句点, 正斜杠, 和 “.com” 按钮. (你可以长按 “.com” 按钮选择常见的域名后缀, 如 “.org” 或 “.net”.)

不支持 HTML5 的浏览器会把 type="url" 完全当作 type="text"处理, 这样你就无需为你所有的网页地址输入需求做向下兼容.

Numbers as Spinboxes

Next up: numbers. Asking for a number is trickier than asking for an email address or web address. First of all, numbers are more complicated than you might think. Quick: pick a number. -1? No, I meant a number between 1 and 10. 7½? No no, not a fraction, silly. π? Now you’re just being irrational.

My point is, you don’t often ask for “just a number.” It’s more likely that you’ll ask for a number in a particular range. You may only want certain kinds of numbers within that range — maybe whole numbers but not fractions or decimals, or something more esoteric like numbers divisible by 10. HTML5 has you covered.

Pick a number, (almost) any number

<input type="number"
       min="0"
       max="10"
       step="2"
       value="6">

Let’s take that one attribute at a time. (You can follow along with a live example if you like.)

That’s the markup side of a number field. Keep in mind that all of those attributes are optional. If you have a minimum but no maximum, you can specify a min attribute but no max attribute. The default step value is 1, and you can omit the step attribute unless you need a different step value. If there’s no default value, then the value attribute can be the empty string or even omitted altogether.

But HTML5 doesn’t stop there. For the same low, low price of free, you get these handy JavaScript methods as well:

Having trouble visualizing it? Well, the exact interface of a number control is up to your browser, and different browser vendors have implemented support in different ways. On the iPhone, where input is difficult to begin with, the browser once again optimizes the virtual keyboard for numeric input.

iPhone rendering input type=number field

In the desktop version of Opera, the same type="number" field is rendered as a “spinbox” control, with little up and down arrows that you can click to change the value.

Opera rendering input type=number field

Opera respects the min, max, and step attributes, so you’ll always end up with an acceptable numeric value. If you bump up the value to the maximum, the up arrow in the spinbox is greyed out.

Opera rendering input type=number field at max value

As with all the other input types I’ve discussed in this chapter, browsers that don’t support type="number" will treat it as type="text". The default value will show up in the field (since it’s stored in the value attribute), but the other attributes like min and max will be ignored. You’re free to implement them yourself, or you could reuse a JavaScript framework that has already implemented spinbox controls. Just check for the native HTML5 support first, like this:

if (!Modernizr.inputtypes.number) {
  // no native support for type=number fields
  // maybe try Dojo or some other JavaScript framework
}

Numbers as Sliders

Spinboxes are not the only way to represent numeric input. You’ve probably also seen “slider” controls that look like this:

Chrome rendering of input type=range field as slider control
Test type="range" for yourself.

You can now have slider controls in your web forms, too. The markup looks eerily similar to spinbox controls:

The spitting image

<input type="range"
       min="0"
       max="10"
       step="2"
       value="6">

All the available attributes are the same as type="number"min, max, step, value — and they mean the same thing. The only difference is the user interface. Instead of a field for typing, browsers are expected to render type="range" as a slider control. At time of writing, the latest versions of Safari, Chrome, and Opera all do this. (Sadly, the iPhone renders it as a simple text box. It doesn’t even optimize its on-screen keyboard for numeric input.) All other browsers simply treat the field as type="text", so there’s no reason you can’t start using it immediately.

Date Pickers

HTML 4 did not include a date picker control. JavaScript frameworks have picked up the slack (Dojo, jQuery UI, YUI, Closure Library), but of course each of these solutions requires “buying into” the framework on which the date picker is built.

HTML5 finally defines a way to include a native date picker control without having to script it yourself. In fact, it defines six: date, month, week, time, date + time, and date + time - timezone.

So far, support is… sparse.

Date picker support
Input TypeOperaEvery other browser
type="date"9.0+·
type="month"9.0+·
type="week"9.0+·
type="time"9.0+·
type="datetime"9.0+·
type="datetime-local"9.0+·

This is how Opera renders <input type="date">:

Opera's type=date picker

If you need a time to go with that date, Opera also supports <input type="datetime">:

Opera's type=datetime picker

If you only need a month + year (perhaps a credit card expiration date), Opera can render a <input type="month">:

Opera's type=month picker

Less common, but also available, is the ability to pick a specific week of a year with <input type="week">:

Opera's type=week picker

Last but not least, you can pick a time with <input type="time">:

Opera's type=time picker

It’s likely that other browsers will eventually support these input types. But just like type="email" and the other input types, these form fields will be rendered as plain text boxes in browsers that don’t recognize type="date" and the other variants. If you like, you can simply use <input type="date"> and friends, make Opera users happy, and wait for other browsers to catch up. More realistically, you can use <input type="date">, detect whether the browser has native support for date pickers, and fall back to a scripted solution of your choice (Dojo, jQuery UI, YUI, Closure Library, or some other solution).

Date picker with fallback

<form>
  <input type="date">
</form>
...
<script>
  var i = document.createElement("input");
  i.setAttribute("type", "date");
  if (i.type == "text") {
    // No native date picker support :(
    // Use Dojo/jQueryUI/YUI/Closure to create one,
    // then dynamically replace that <input> element.
  }
</script>

OK, this one is subtle. Well, the idea is simple enough, but the implementations may require some explanation. Here goes…

Search. Not just Google Search or Yahoo Search. (Well, those too.) Think of any search box, on any page, on any site. Amazon has a search box. Newegg has a search box. Most blogs have a search box. How are they marked up? <input type="text">, just like every other text box on the web. Let’s fix that.

<form>
  <input name="q" type="search">
  <input type="submit" value="Find">
</form>

 ⇜ New-age search box

Test <input type="search"> in your own browser. In some browsers, you won’t notice any difference from a regular text box. But if you’re using Safari on Mac OS X, it will look like this:

Safari/Mac rendering input type=search field

Can you spot the difference? The input box has rounded corners! I know, I know, you can hardly contain your excitement. But wait, there’s more! When you actually start typing into the type="search" box, Safari inserts a small “x” button on the right side of the box. Clicking the “x” clears the contents of the field. (Google Chrome, which shares much technology with Safari under the hood, also exhibits this behavior.) Both of these small tweaks are done to match the look and feel of native search boxes in iTunes and other Mac OS X client applications.

Typing in a type=search field in Safari

Apple.com uses <input type="search"> for their site-search box, to help give their site a “Mac-like” feel. But there’s nothing Mac-specific about it. It’s just markup, so each browser on each platform can choose to render it according to platform-specific conventions. As with all the other new input types, browsers that don’t recognize type="search" will treat it like type="text", so there is absolutely no reason not to start using type="search" for all your search boxes today.

Professor Markup Says

By default, Safari will not apply even the most basic CSS styles to <input type="search"> fields. If you want to force Safari to treat your search field like a normal text field (so you can apply your own CSS styles), add this rule to your stylesheet:

input[type="search"] {
  -webkit-appearance: textfield;
}

Thanks to John Lein for teaching me this trick.

Color Pickers

HTML5 also defines <input type="color">, which lets you pick a color and returns the color’s hexadecimal representation. No browser supports it yet, which is a shame, because I’ve always loved the Mac OS color picker. Maybe someday.

Form Validation

Form validation support
IEFirefoxSafariChromeOperaiPhoneAndroid
·4.0+5.0+6.0+9.0+··

In this chapter, I’ve talked about new input types and new features like auto-focus form fields, but I haven’t mentioned what is perhaps the most exciting part of HTML5 forms: automatic input validation. Consider the common problem of entering an email address into a web form. You probably have some client-side validation in JavaScript, followed by server-side validation in PHP or Python or some other server-side scripting language. HTML5 can never replace your server-side validation, but it might someday replace your client-side validation.

There are two big problems with validating email addresses in JavaScript:

  1. A surprising number of your visitors (probably around 10%) won’t have JavaScript enabled
  2. You’ll get it wrong

Seriously, you’ll get it wrong. Determining whether a random string of characters is a valid email address is unbelievably complicated. The harder you look, the more complicated it gets. Did I mention it’s really, really complicated? Wouldn’t it be easier to offload the entire headache to your browser?

Opera validates type=“email”

error message on invalid type=email field

That screenshot is from Opera 10, although the functionality has been present since Opera 9. The only markup involved is setting the type attribute to "email". When an Opera user tries to submit a form with an <input type="email"> field, Opera automatically offers RFC-compliant email validation, even if scripting is disabled.

HTML5 also offers validation of web addresses entered into <input type="url"> fields, and numbers in <input type="number"> fields. The validation of numbers even takes into account the min and max attributes, so browsers will not let you submit the form if you enter a number that is too large.

error message on invalid type=number field

There is no markup required to activate HTML5 form validation; it is on by default. To turn it off, use the novalidate attribute.

Don’t validate me

<form novalidate>
  <input type="email" id="addr">
  <input type="submit" value="Subscribe">
</form>

Browsers are slowly implementing support for HTML5 form validation. Firefox 4 will have complete support. Unfortunately, Safari and Chrome have shipped an incomplete implementation that may trip you up: they validate form controls, but they don’t offer any visible feedback when a form field fails validation. In other words, if you enter an invalid (or improperly formatted) date in a type="date" field, Safari and Chrome will not submit the form, but they won’t tell you why they didn’t submit the form. (They will set focus to the field that contains the invalid value, but they don’t display an error message like Opera or Firefox 4.)

Required Fields

<input required> support
IEFirefoxSafariChromeOperaiPhoneAndroid
·4.0+··9.0+··

HTML5 form validation isn’t limited to the type of each field. You can also specify that certain fields are required. Required fields must have a value before you can submit the form.

The markup for required fields is as simple as can be:

<form>
  <input id="q" required>
  <input type="submit" value="Search">
</form>

Test <input required> in your own browser. Browsers may alter the default appearance of required fields. For example, this is what a required field looks like in Mozilla Firefox 4.0:

default appearance of required fields in Firefox

Furthermore, if you attempt to submit the form without filling in the required value, Firefox will pop up an infobar telling you that the field is mandatory and can not be left blank.

Further Reading

Specifications and standards:

JavaScript libraries:

Useful articles:

This has been “A Form of Madness.” The full table of contents has more if you’d like to keep reading.

Did You Know?

In association with Google Press, O’Reilly is distributing this book in a variety of formats, including paper, ePub, Mobi, and DRM-free PDF. The paid edition is called “HTML5: Up & Running,” and it is available now. This chapter is included in the paid edition.

If you liked this chapter and want to show your appreciation, you can buy “HTML5: Up & Running” with this affiliate link or buy an electronic edition directly from O’Reilly. You’ll get a book, and I’ll get a buck. I do not currently accept direct donations.

Copyright MMIX–MMX Mark Pilgrim