现代 CSS

聊聊input元素

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

在Web中通过HTML的表单和用户交互是最常见的方式之一,同时HTML的表单也是Web最为复杂的交互部分之一。正因为其复杂,Web中的表单开发也令开发较为头疼。另外,Web表单涵盖了很多部分,除了我们最为熟悉的的<input><button><select>等表单元素之外,还有其他新的表单元素,比如在HTML5新增的<datalist><output><progress><meter>等元素。简单地说,这些元素可以构建出人与机对话或交互用户界面,然而由于历史和技术上的原因,如何充分发挥它们的潜力并不总是显而易见的。就拿<input>来说吧,要想彻底了解和懂得用<input>并不是易。既然如此,我们在这篇文章中就来和大家聊聊<input>中的故事。

HTML中的<input>元素

HTML的<input>元素用于为基于Web的表单创建交互式控件,以便接受来自用户的数据;可以使用各种类型的输入数据和控件小部件,具体取决于设备和User Agent

文章开头就提到过,<input>元素是表单中最为复杂的元素之一。这么说是有其一定原因的,不说别的,就其类型(type)就有近二十种。

既然如此,我们就从<input>元素的type属性开始吧。

<input>的类型

<input>元素的工作方式相当程度上取决于其type属性的值,不同的type值所取的作用不一样。到目前为止,type值有差不多22种不同的值,它们在不同的浏览器中的表现也不尽相同,尤其在移动端(不同的值可以唤起不同的键盘类型)。我们来看看type的取值以及其所取的作用。

注意datetime类型已被弃用

文本类型(text

<input>元素的type取值为text是大家最为熟悉的一种输入框类型,即文本输入框,这也是type的默认值:

Text

它也被称为单行文本输入框。如果在<input>中未显式设置type值时,将会取值text(该值也是默认值),或者当<input>设置为一个不支持的类型时,浏览器也会取值text

Default Text

在希望用户输入单行值的任何地方都可以使用typetext<input>,并且没有更具体的输入类型可用于收集该值。

注意,type="text"时,输入中的换行会被自动去除

密码类型(password

<input>元素的type取值为password时被称为密码输入框。主要用于让用户更加安全的输入密码。这个元素是作为一行纯文本编辑器控件呈现的,其中文本被遮蔽以致于无法读取,通常通过诸如星号*或点符号替换每个字符来实现。这个符号会根据用户的浏览器和操作系统来具体显示哪个。

Password

<input>type取值为textpassword时,在未显式值或用户未输入值时,它们看上去是一样的,当用户输入值时,显示效果是有差异的,text对应的文本框输入的值可见,password对应的密码框输入的值不可见,会以*替代:

复选框类型(checkbox

<input>元素的type取值为checkbox时,表示的是一个复选框,允许你为表单中(或不在表单中)的提效项选择一个单一值。

Checkbox (未选中) (选中)

当有多个复选框可以同时选择多个值,也可以通过点击取消。如果表单在提交时,复选框未选中时,则提交的值并非为value="unchecked";此时的值不会被提交到服务器。

单选按钮类型(radio

<input>type取值为radio时,表示是的一个单选按钮。被称为单选按钮,是因为其外观和操作方式与老式无线电上的按钮类似:

该元素默认渲染为小型圆圈,当选中时,圆圈中会有一个不同颜色的点(这种状态是激活状态)。

Radio (未选中) (选中)

单选按钮和复选框不同,单选按钮只能选择一个值,再次单击也不能取消:

Radio (尝试着选中,选中之后单击也不能取消)

如果想取消单击过的单选按钮,只能通过单击相同name的其他单选按钮来取消当前选中的单选按钮(一个单选按钮组由具有相同name属性的单选按钮组成)。

Choose your language Chinese English Spanish Japnese

单选按钮和复选框有一个最重要的区别:单选按钮为选择其中一项而设计,然而复选框允许你开启或关闭单个值。每个独立的单选按钮大致上是 Boolean控件(是或不是)。

按钮类型(button

<input>元素的type取值为button时,被称为按钮。它是<input>元素的特殊版本,被用来创建一个没有默认值的可点击按钮。

Button

它是一个单纯的按钮,没有默认的事件。

注意:<input>取值为type现基本被<button>元素取代

表单重置类型(reset

<input>元素的type取值为reset时,被称为重置按钮,其渲染结果和type="button"类似:

Button and Reset

type取值为reset时虽然渲染的结果上类似于button,但它们有一个本质上的差异,那就是reset默认带有click事件,可以用于将表单中的所有输入重置为其初始值。另外,如果type="reset"<input>,如果未显式设置value的值,会有一个默认值,即“reset(或重置)”,不同的浏览器或操作系统取值不同。

在实际使用的时候应该尽量避免在表单中使用reset按钮。因为其没有啥实用性,甚至更有可能使用户误点重置已填好的选项而感到沮丧。

表单提交类型(submit

<input>元素的type取值为submit时,被称为提交按钮。它和type取值reset类似,不同的是,用户代理会试图提交表单到服务器。

Submit and Reset

图片类型(image

<input>元素的type取值为image时,被称为图形提交按钮。它的行为既像typesubmit<input>又类似<img />元素。使用type="image"时,在<input>元素上必须使用src属性来义图像的来源,同样可以使用alt来描述按钮上下文信息。

Image

文件类型(file

<input>type取值为file时,无论用户的设备或操作系统是什么,它都会提供一个按钮,打开一个允许用户选择文件的文件选择对话框。用户可以在这个文件选择对话框中选择一个或多个文件以提交表单的方式上传到服务器上,或者通过JavaScript的File API对文件进行操作。

Load your file

隐藏类型(hidden

<input>type取值为hidden时,被称为隐藏类型<input>type取值为hidden时,在页面上是不会渲染该控件,但其值仍会提交到服务器上。

Hidden (其实这里有一个你看不见的input元素)

<input>type取值为hidden时有一些优势,可以让Web开发者存放一些用户不可见,不可改的数据,而用户在提交表单时,这些用户看不到(被隐藏)的数据会一并发送出去,提交到服务器上。比如,正被请求或编辑的内容的ID,或是一个唯一的安全令牌。这些隐藏的<input>元素在渲染完成的页面中是没有办法可以使它重新变得可见的。

颜色类型(color

<input>元素的type取值为color时构建的是一个特定种类的输入框,用来创建一个允许用户使用颜色拾色器,或输入兼容CSS语法的颜色代码的区域。

Color

此元素的外观会因浏览器不同而不同,可能是简单的验证颜色输入格式的文本框,也可能使用平台原生或自定义样式的颜色拾色器。其UI除要能接收文本格式的颜色外,未要求其他特性。

如果你没有手动指定的话,其默认值为#000000,即黑色。输入必须为七个字符,以 #字符开始的十六进制颜色表示法。如果你输入的颜色是其他格式,比如rgb()rgba(),需要在设定value值时必须将其转换为这种格式。

Color

比如上面的示例,显式设置值为rgb(255 0 0),但浏览器不识别(只识别十六进制颜色表示),所以会取默认值:

使用type="color"<input>能提供用户较好的交互方式,用户可以通过强大的颜色拾色器选择自己需要的颜色:

日期类型(date

<input>元素的type的值为date时,被称为日期类型输入框。该类型输入框会创建一个让用户输入一个日期的输入区域,可以使用自动验证内容的文本框,也可以使用特殊的日期选择器界面。其值包括年、月、日,但不包括时间。

Date

通常来说控件的UI界面会因浏览器的不同而有变化,到目前为止控件还不被所有浏览器支持,不支持的浏览器当中,该输入框会被优雅的降级为type="text"的普通输入框。

日期控件,一开始听起来可能觉得很方便。它们不仅提供了一个简单的日期选择UI界面,还规范了发往后台的数据格式,无论用户在什么区域。

日期时间类型(datetime-local

<input>元素的type取值为datetime-local可以创建让用户方便输入日期和时间的输入框,其包括年、月、日以及时和分,但不支持输入秒。

Datetime-local

该类型所使用的是用户所在时区。

月份类型(month

<input>元素的type取值为month可以创建让用户方便输入年或月的一个输入框

Month

周类型(week

<input>元素的type取值为week时可以创建让用户方便输入年份以及该年(即第1周到5253周)的ISO 8601周数字

Week

时间类型(time

<input>元素的type取值为time时可以创建让用户方便输入时间(小时、分以及秒)。

Time

我们可以使用typedatetime的两个<input>,达到typedatetime-local等同的效果:

Date & Time & Datetime-local

范围类型(range

<input>元素的type取值为range时允许用户指定一个数值,该数值必须不小于给定值,并且不得大于另一个给定值。但是,精确值并不重要。通常使用滑块来拖动。

Range

数字类型(number

<input>元素的type取值为number时,用来创建方便用户输入一个数字的文本框。该类型控件内置了验证拒绝用户输入非数字字符。

Number

浏览器会在输入框得到焦点(或鼠标悬浮时)会在右侧提供上下箭头,让用户根据step属性设置的值(默认为1)增加或减少输入的值。

电话类型(tel

<input>type取值为tel时,用来创建用户方便输入电话号码的文本框

Tel

不过,该类型和type取值为emailurl时有所不同,在提交表单之间,输入值不会被自动验证为特定格式,因为世界各地的电话号码格式差别很大。

网址类型(url

<input>元素的type取值为url时,用来创建方便用户输入URL,也被称为网址输入框

Url

从表面的渲染结果来看,它和type="text"非常的相似,但它们有一个本质上的区别,url类型输入框内置了验证参数。当用户提交表单时,会自动验证输入值,以确保它是空的或格式正确的URL。另外一点,在移动端上,它们两唤起的软键盘格式也不一样:

不同的设备和系统上会有所差异

电子邮件类型(email

<input>元素的type取值为emial时,用来创建方便用户输入电子邮箱地址,也常被称为电子邮箱输入框

Email

它和url类型相似,也内置了验证功能。在提交表单时会自动验证输入的值是否符合电子邮箱地址格式(非空值或符合电子邮箱地址格式)。其效果同样看上去和text类型类似。

搜索类型(search

<input>元素的type取值为search时,创建的是一种特定类型的输入框,常被称为搜索输入框。该控件专门是用来为用户输入搜索查询而设计的。

Search

search 类型与 text 类型的输入非常相似,不同之处在于它们专门用于处理搜索项。 它们的行为基本相同,但是用户代理可以默认选择不同的样式。

可以说丰富的type类型给了开发者有更多的选择,可以告别千篇一律的text类型了。特别是在现代环境之下,用户终端设备种类越来越多,提供给用户输入数据的单一文本框(type="text")已经无法满足用户。你可能也发现了,同样看上去像普通的文本输入框,在移动端上唤起的软键盘就一样。特别是时间类型的输入框:

这些都大大提高了用户的体验,也能快速帮助用户输入信息。注意,不同的设备平台以及系统,渲染type类型时都会有所差异,甚至交互行为也会有所差异:

如果你感兴趣,可以尝试着用你自己的移动设备打开下面这个Demo

上面示例没有对input添加任何样式,你在设备浏览器中看到的渲染效果可以说是最原始(原生态的)<input>不同类型的控件UI风格

另外,WHATWG Living标准<input>元素提出了一个inputmode属性(该属性在W3C 5.2版本规范又被移除)可以用来指定输入框的输入模式。有点类似于我们设置不同的type值在移动端能唤起起不同的软键盘。

换句话说,当你指定<input>type值为text时,显式设置了inputmode的值,可以用来改善用户体验,唤起不同方式软键盘,给用户提供最合适的输入方式。目前inputmode接受的值有nonenumericteldecimaltelurlsearch几个值。inputmode取不同值时,在不同设备平台或系统下效果不一样,大致如下:

inputmode取值为none时,在Android系统的浏览器中不会显示键盘;而在iOS 12.2系统中将显示其默认的字母数字键盘。因此在iOS系统中可以将inputmode的值设置为none来重置iOS系统下键盘。

属性(Attributes)

表单中的<input>元素有很多属性,前面说的typeinputmode都是众多属性中的一种。除了type之外还有很多强大的属性,而且input的属性有一个特点,不同的type类型的<input>元素具有的属性还有差异。简单地说,大部分适用于<input>元素的属性只用作用于特定一组type。此外,一些属性作用于<input>的方式取决于其type属性,不同的type有不同的效果。

<input>元素它有一些通用的属性,也是大家较为熟悉的属性,比如maxlengthminlengthsizereadonlyrequiredmultiplepatternminmaxsteplistvalueplaceholder。而且在HTML5中引入了一些新的属性,比如autofocusformrequiredautocomplete等。

从HTML的规范中我们可以得知,<input>元素的属性(Attributes)的属性主要分为四大类,即内容属性(Content Attributes)IDL属性(Interface Description Language Attributes)方法(Methods)事件(Events)

不同的的type类型有不同的属性,具体的如下图所示:

接下来挑选有意思的属性的简单介绍一下,更为详细的介绍,建议大家花时间阅读HTML规范

全局属性

全局属性(Global Attributes)是所有HTML元素共有的属性;它们可以用于所有元素。

<input>是HTML众多元素中之一,因此全局属性都可以运用于<input>元素上。比如我们熟悉的idclassstyletabindextitle等。

值(value

所有的<input>都有一个value属性,用来设置表单控件的值。在大部分的类型的<input>中设置value值,都会呈现给用户,比如我们熟悉的text,在value中指定值时,就是在输入框中看到。

<input type="text" value="我是输入框的值" />

你在浏览器中看到的效果如下:

Value

这也可以说是文本框的值,不过用户可以重新键入自己所需要的值。但有些<input>value值,用户是看不到的,比如checkboxradio等。虽然这些值用户看不到,但同样会以名字/值对的形式随表单一起提交到服务器。

使用JavaScript的时候,可以通过Element.value获取<input>元素的value值。如果未显式设置value值,那么取出的值是一个空字符串,如果显式的设置了值,则是value属性指定的值。但radiocheckbox有所不同,如果没有设置value值时,其取出的值并不是空字符串,而是字符串on

占位符(placeholder

占位符(placeholder属性表示一个简短的提示(一个单词或一句话),用于在<input>未显式设置value时能更好的帮助用户根据占位符提示语句输入正确格式的数据。

当你在<input>元素上显式设置placeholder时,他在浏览器中显示的效果和设置了value值是类似,只是占位符的文本颜色更淡一些(在未用CSS做特殊处理的时候):

Value vs. Placeholder

如果<input>元素同时显式设置valueplaceholder值时,将会以value值为主:

Value vs. Placeholder

placeholder属性仅适用于type属性为textsearchtelurlemail,但它也可以在其他类型的<input>元素上显式设置,不过其值会被忽略而以。另外,在JavaScript中使用element.value时不会将placeholder值赋予给value,但我们可以使用element.placeholder来获取该属性的值。

另外有一点需要注意,在给placeholder属性设置值时,该值不能包含U+000A换行符(LF)或U+000D回车符(CR)字符。

由于placeholder可以起到提示作用,因此有些设计师在设计表单时,将会使用该属性来替代表单中的<label>标签。但这样做并不是明智的。因为他们的作用不同:<label>标签是用来描述表单元素的角色,也就是说,它展示预期的信息;而placeholder属性是提示用户内容的输入格式。最为关键的是,某些情况下placeholder属性对用户不可见,所以没有它时有可能会造成用户不好理解表单控件要填入的内容。

就Web可访问性而言,如果仅给<input>元素设置placeholder值用来作为提示信息(我的意思是未使用<label>标签、nametitleARIA相关属性),那么对于部分用户群体(比如使用屏幕阅读器的用户)是非常地不友好。屏幕阅读器大部分情况下是读不到placeholder的值,这样就会让这部分用户群体不知道这个控件是用来做什么的,应该填入什么信息。

在社区中也有很多这方面的讨论,不管是交互方面还是Web可访问性方面。如果你感兴趣的话,可以花点时间阅读下面文章:

名称(name

name属性是用来定义表单控件名称,并在提交表单时连同表单控件的值一起提交。它是为提交表单而与无素关联的名称/值对,如name="book"name属性可以用在所有类型的<input>上,而且name的值不能是空字符串,并且应该是唯一的,除非是在radiocheckbox的情况下。

对于单选按钮radio来说,如果它的name是唯一性的话,那么它选中之后就无法取消:

换句话说,对于每组单选按钮(radio)中的所有单选按钮的name值应该使用相同的值,这是因为单选按钮一次只能选择同一组中的一个单选按钮:

<input type="radio" id="css" name="lange" />
<label for="css">CSS</label>
<input type="radio" id="javascript" name="lange" />
<label for="javascript">JavaScript</label>
<input type="radio" id="html" name="lange" />
<label for="html">HTML</label>

对于复选框(checkbox)可以像单选按钮radio一样,为同一组复选框设置相同name的值,但并不建议这样做,我们应该尽可能的为每个复选框设置唯一的name值。

我们在构建表单控件时,对于视力正常的用户来说,理解一组基于上下文的复选框或单选按钮的用途通常是很容易的。然而对于屏幕阅读器用户来说,情况并非总是如此。我们应该以清晰的描述名称将它们分组在一起,这样可以消除屏幕阅读器用户体验的模糊性,从而创建更易于访问的产品。在编码时,我们可以考虑以下方式:

  • 将同一组checkboxradio放在<fieldset><legend>标签内
  • 使用ARIA的role="group"(角色)和aria-labelaria-labelledby(属性),其中aria-labelledby指向组中每个checkboxradioid

比如像下面这样:

<fieldset>
    <legend>Select one or more colors</legend>
    <input type="checkbox" name="red" id="red" value="red"> <label for="red">Red</label>
    <input type="checkbox" name="blue" id="blue" value="blue"> <label for="blue">Blue</label>
    <input type="checkbox" name="yellow" id="yellow" value="yellow"> <label for="yellow">Yellow</label>
</fieldset>

<fieldset>
    <legend>Select one colors</legend>
    <input type="radio" name="color" id="red2" value="red"> <label for="red2">Red</label>
    <input type="radio" name="color" id="blue2" value="blue"> <label for="blue2">Blue</label>
    <input type="radio" name="color" id="yellow2" value="yellow"> <label for="yellow2">Yellow</label>
</fieldset>

效果如下:

Select one or more colors
Select one color

最长(maxlength)和最短(minlength

如果<input>元素的type值是textemailsearchpasswordtelurl时,maxlengthminlength属性分别用来限制用户可以输入的字符个数(按照Unicode编码方式计数)。其中maxlength用来限制最多字符个数,minlength用来限制最少字符个数。如果不指定这两个属性值,那么用户可以输入任意多的字符数;如果值为负数,则属性的值无效,用户可以输入任意多的字符数。

如果用户显式的在<input>上显式的设置了maxlength的值,那么当用户输入的字符数超过这个值时,则无法继续输入。比如在一些限制字符输入数的场景就非常的实用,比如下面这样的场景:

maxlength的值可以大于size属性的值,另外它和size有着本质上的区别,其中maxlength是限制字符输入数,而size是像素值,指的是字符长度,用来设置控件的初始大小。

如果用户显式的在<input>上显式的设置了minlength的值,虽然用户可以输入任意多个字符数,但如果输入的字符数(文本长度)小于minlength(UTF-16代码单位)长度,验证将失败,从而阻止表单提交。

maxlengthminlength运用在非textemailsearchpasswordtelurl<input>元素上时,这两个属性将会被忽略。

初始大小(size

size属性值是一个大小0的整数,主要用来控制控件的初始大小(以像素为单位)。但当type属性的值为textpassword时,它表示输入的字符的长度。从HTML5开始,该属性仅适用于当type属性为textsearchtelurlemailpassword,如果运用于其他类型的<input>,该属性将会被忽略。如果未显式指定该属性的值,则会使用默认值20

需要特别注意,size是用来控制控件初始大小(一般指宽度),我们可以使用CSS重新设置控件大小。它和maxlength属性最大的区别是,maxlength限制用户输入的最大字符数,size是用来初始化控件的初始长度,指的是字符长度。一个是“字符数”,一个是“字符长度”,但结果是完全不同的

<input type="text" id="maxlength" maxlength="10" >
<input type="text" id="size" size="10" >

最小值(min)和最大值(max

有一些<input>表单控件可以约束用户要指定的区域范围内设置值。这个区域范围是通过minmax来指定的。这两个属性主要运用于typedatedatetime-localmonthtimenumberrangeweek

  • date:限制用户的可选日期范围
  • datetime-local:限制用户可选择的日期、时间范围
  • month:限制用户选择月份范围
  • time:限制用户选择的时间范围
  • week:限制用户可以选择的有效周数
  • number:限制用户可以输入的数字范围
  • range:限制滑块范围值

例如:

<input type="date" id="birthday" name="birthday" min="1900-01-01" max="2020-12-31" />

<input id="party" type="datetime-local" name="partydate" min="2017-06-01T08:30" max="2020-09-30T16:30" />

<input id="bday-month" type="month" name="bday-month" min="1900-01" max="2020-12" />

<input id="week" type="week" name="week" min="2017-W01" max="2020-W52" />

<input id="time" type="time" name="time" min="00:00" max="23:59" />

<input type="number" placeholder="01" step="1" min="0" max="12" id="number" />

<input type="range"  step="1" min="0" max="100" id="range" />
尝试选择操作下面的表单控件

在上述提到能设置minmax的非range<input>控件,虽然会有一个区域范围限制,但用户依旧可以手动输入超过这个范围的值,但表单提交时则无法通过约束验证。

步值(step

step属性对于数字输入类型(包括datemonthweektimedatetime-localnumberrange)有效。step属性是一个数字。

对于数字输入类型控件,当其鼠标悬浮在控件上或控件得到焦点时,控件右侧有箭头出来,可以让用户通过简头来向上或向下按step设置的值进行递增或递减:

如果没有显式设置step的值时,numberrange的默认值为1datemonthdatetime-localweek的默认值为1个单元类型(1天、1个月、1天和1周),time的默认值为1分钟(60秒)。

step的值必须是正数(整数或浮点数)或特殊的值any,这意味着没有step,并且允许何值(排除其他约束,比如minmax)。

我们拿typenumber的控件来举例,相对简单一点:

<input type="number" />

用户每次点击向上(向下)箭头,会按默认值递增(减)1

<input type="number" step="3" />

用户每次点击向上(向下)箭头,会按step指定的值递增(减)3

<input type="number" step="3" min="2" />

用户每次点击向上箭头,会按step指定的值递增3,但第一次点击时将会是min的值,然后每次递增3。如果用户第一次点击下的箭头,会取min的值,然后再次点击时无效。不过用户操作多次向上箭头,再操作向下简头时,会按step的值递减,直到min的值停止递减。

<input type="number" step="3" min="2" max="20" />

这个和上面的示例类似,不同的是递增的值不会超过20

<input type="number" step="any" min="2" max="20" />

step设置any时会根据控件类型来做递增或递减,比如上面的number,会按默认值1递增或递减。

注意,不同类型的控件对于step的操作不同,特别是有minmaxvalue

自动聚焦(autofocus

<input>元素HTML中众多可聚焦元素之一。对于可聚焦元素而言,当元素得到焦点时会有不同的样式提示。在HTML中,我们可以给可聚焦元素设置autofocus属性,当然该属性也可以用于<input>元素。

autofocus是一个布尔值属性,允许你指定的表单控件在页面加载时具有焦点(自动获取焦点),除非用户将其覆盖,例如在不同的控件中输入内容。autofocus属性不仅局限于用在表单控件元素上,只要是HTML中的可聚焦元素都适用于该属性。只不过,整个HTML文档只有一个可聚焦元素可以使用autofocus属性。如果文档中同时有多个可聚焦元素显式设置了autofocus属性,则离文档根元素<html>最近的将生效。不过,该属性不能用于typehidden<input>,因为type="hidden"<input>无法获取焦点(不能被聚集)。

autofocus属性是一个布尔属性,如果元素显示设置了该属性,则其值是true,未显式设置,其值是false

注意,使用autofocus属性会降低用户的可用性和可访问性。辅助技术的用户(比如使用屏幕阅读器用户)可能会受到负面影响,因为它的使用覆盖了辅助技术的默认行为,即在视口中显示文档顶部的内容,或者从文档开始就朗读内容。有认知障碍的用户也可能在页面加载时被意想不到的焦点移动所迷惑。

不过我们使用aira-describedby可能会对Web的可用性和可访问性会有所改善。比如下面这个示例:

<div role="alertdialog" aria-labelledby="acc_name" aria-describedby="acc_desc">
    <h2 id="acc_name">Are you sure?</h2>        
    <p id="acc_desc">Once you delete this thing, it's gone forever!</p>
    <button autofocus>Do not delete</button>
    <button>Delete</button>
</div>

当这个警告框加载时,autofocus会让”不删除按钮“自动获取焦点。但是,由于屏幕阅读器对autofocus功能有所限制。我们可以使用aria-describedby让屏幕阅读器给用户提供更完善的信息。就上例来说,屏幕阅读器可以能会像下面这样朗读:“Are you sure? dialog. Once you delete this thing, it’s gone forever! Do not delete, button.”

就可访问性而言,这里额外提一句,本来焦点元素的控制(焦点顺序)对于可访问性和可用性都是较为复杂的,在表单的创建中更是如此。有一个较好的焦点控制的表单,对于用户来说是非常有幸的,因为这样能帮助用户节省很多时间,并且能快速准确无语的完成相关的数据提交

自动填充(autocomplete

用户代理有时具有帮助用户填写表单的功能,例如根据用户以前输入的信息来自动补全。在<input>中可以使用autocomplete内容属性向用户代理提示如何或是否提供这样的功能。

这个属性有两种使用方式。当使用autofill期望属性(Autofill Expectation Mantle)时,autocomplete属性描述用户期望输入的内容。当使用Autofill Anchor Mantle时,autocomplete属性描述给定值的含义。

autocommplete可用的属性值有很多种,常见的是offon

  • off: 用户必须手动填值,或者该页面提供了自己的自动补全方法。浏览器不对此字段自动填充
  • on: 浏览器可以根据用户先前的填表情况对此字段自动填值

注意,当type属性的值是hiddencheckboxradiofile,或为按钮类型(buttonsubmitresetimage),则本属性被忽略。

禁用(disabled

disabled属性是一个布尔值属性,可以运用于除typehidden的所有<input>控件上。如果在<input>上显式设置了该属性,岀该控件不可用。

显式设置了disabled属性的元素,用户既不能编辑,也不能让控件得到焦点。如果在<form>元素上显式地设置了disabled,则表单自身以及其所有后代控件元素都不能编辑,也不参与约束验证,甚至不能被提交。浏览器通常会使显式设置了disabled的控件变灰,不会接收任何浏览器事件,比如鼠标点击或与焦点相关的事件。

disabled属性指示用户无法与控件或其后代控件交互。如果未指定此属性,则控件从包含的元素(比如fieldset)继承其设置;如果不存在disabled属性集的包含元素,并且控件自身没有该属性,则启用该控件。

选择你喜欢的颜色
选择你喜欢的颜色(Disabled)

注意,正如上面示例所示,<fieldset>显式设置了disabled时,除了<legend>之外的所有表单控件都将被禁用

当一个控件运用了disabled属性时,:disabled伪类也会被应用;相反,支持disabled属性的元素未显式设置disabled时,:enabled伪类与之匹配使用。

input:disabled {
    color: -internal-light-dark-color(rgb(84, 84, 84), rgb(170, 170, 170));
    cursor: default;
}

input:enabled {
    color: #2b2;
}

也就是说,我们可以使用:disabled:enabled伪类选择器,给同一控件在不同禁用(disabled)和非禁用(enabled)状态下提供不同的UI效果。

如果元素不支持disabled属性,那么这个属性将不会起任何作用,同时也不会匹配:disabled:enabled伪类选择以及他们指定的CSS也将不会运用到元素上。

disabled属性和readonly(只读)的区别在于,设置了readonly的控件(只读控件)仍然可以工作,并且依旧可以获取焦点;而设置了disabled的控件不能无法获得焦点,不能与表单一起提交,并且通常在启用它们之前控件不能工作。

<input type="text" disabled value="disabled input" />
<input type="text" readonly value="readonly input" />

效果如下:

Disabled vs. Readonly

另外,由于设置了disabled之后,用户不能改变它的值,即使在<input>控件上指定了required属性,同样的不会有任何影响。

Disabled vs. Readonly vs. Required

只读(readonly

readonly属性disabled类似,也是一个布尔值属性,可以让用户无法输入或编辑<input>控件中的内容,因为用户不能编辑输入,所以该元素不参与约束验证。

readonlydisabled也有不同之处,比如说,设置了readonly元素的的控件虽然不能输入编辑,但还是可以获得焦点。另外,readonly属性只能用于typetextsearchurltelemailpassworddatemonthweektimedatetime-localnumber这些输入型控件。但对于typehiddenrangecolorcheckboxradiofile和按钮类型(buttonsubmitresetimage)并不适用,即使显式设置了,该属性也将被忽略。

如果在适用于readonly<input>上显式设置了该属性,将会匹配:read-only伪类,如果未显式设置readonly<input>控件则会自动匹配:read-write伪类

input:read-only { 
    background: orange; 
}
input:read-write { 
    background: cyan; 
}

必填(required

required属性也是一个布尔类型。在表单设计中总是有些控件是用户必须要填的,如果不填的话,表单提交是不能成功的。比如一个注册表单,用户名、密码等<input>都是必填的。特别是需要用户填的项目较多的表单中,该属性非常有用。如果在<input>控件中显式设置了required,就表示用户必须输入值,然后才能提交表单。

required属性可以用于typetexturltelemailpassworddatemonthweektimedatetime-localnumbercheckboxradiofile<input>。该属性则不能用于colorrangehidden和按钮类型(buttonsubmitresetimage)等类型的<input>

注意,colorrange不支持required,那是因为color的默认值为#000000range的lfyw默认值为minmax之间的中间值。

如果元素上显式设置了required属性则会匹配:required伪类选择器,反之则会匹配:optional伪类选择器

对于相同命名的单选按钮组,如果组中的单个单选按钮具有required属性,则会必须选中该组中的单选按钮,但不一定要应用具有该属性的单选按钮。

<fieldset>
    <legend>Select Gender:</legend>
    <label><input type="radio" name="gender" value="male" required>Male</label>
    <label><input type="radio" name="gender" value="female">Female</label>
    <label><input type="radio" name="gender" value="other">Other</label>
</fieldset>
Select Gender:

为了避免混淆是否需要一个单选按钮组,建议在组中的所有单选按钮上指定required属性:

<fieldset>
    <legend>Select Gender:</legend>
    <label><input type="radio" name="gender" value="male" required>Male</label>
    <label><input type="radio" name="gender" value="female" required>Female</label>
    <label><input type="radio" name="gender" value="other" required>Other</label>
</fieldset>

实际上,在一般情况下,我们应该尽可能避免让单选按钮组在一开始就没有任何选中的控件,因为这是用户无法返回的状态,因此通常被认为是糟糕的用户界面。

对于同一组的复选框,required的使用和radio相似:

<fieldset>
    <legend>Choose your interests</legend>
    <label>
        <input type="checkbox"  name="interest" value="coding" required/>Coding
    </label>
    <label>
        <input type="checkbox"  name="interest" value="music" required/>Music
    </label>
    <label>
        <input type="checkbox" name="interest" value="art" required />Art
    </label>
</fieldset>
Choose your interests

对于<input>控件,特别是设置了required的控件,我们可以通过:required:optional来设置不同的样式,给用户提供更好的体验:

选中(checked

checked属性可用于typeradiocheckbox<input>元素。它也是一个布尔值,如果给元素显式设置了checked表示该控件被选中,处于选中状态。

<input type="checkbox"  name="interest" checked value="coding" required/>Coding
<input type="radio" name="gender" value="female" checked>Female
Choose your interests
Select Gender:

radiocheckbox显式设置了checked属性的话,<input>元素就能匹配:checked伪类选择器,可以基于这个状态,为选中状态的元素设置样式。

列表(list

list属性用于标识列出向用户建议预定义选项的元素。如果显式设置了list属性,它的值必须是同一文档中<datalist>元素id值。

<input name="country" list="country_name">
<datalist id="country_name">
    <option value="Afghanistan">
    <option value="Albania">
    <option value="Algeria">
    <option value="Andorra">
    <option value="Armenia">
    <option value="Australia">
    <option value="Austria">
    <option value="Azerbaijan">
    <!-- etc. -->
</datalist>

效果如下:

list & datalist

<datalist><option>只是预设给<input>输入的一些建议(值),与list不兼容的列表中的任何值都不包括在建议的选项中。注意,这里提供的值是建议,而不是要求。用户可以从这个预设列表中选择或自己输入值。

typetextsearchurlemaildatemonthweektimedatetime-localnumberrangecolor<input>元素可以使用list属性。但type值为hiddenpasswordcheckboxradiofile和按钮类型(buttonresetsubmitimage)不能使用list

<input>元素显式设置了list属性,并且有相应匹配的<datalist>时,<input>控件在获得焦点时,双击或在数据输入时,将出现一个下拉菜单,由作为<datalist>的子元素option内容填充。

注意,不同type<input>list配合<datalist>展示的效果会有所差异:

list & datalist

模式(pattern

pattern属性<input>元素中非常有意思的一个属性,它的值可以是正则表达式,相应的正则表达式可以用来验证用户在对应的<input>中输入的内容是否符合对应type的值。就目前为止,pattern属性主要适用于textsearchtelurlemail等类型。

在指定的pattern属性时,它是一个正则表达式,输入的值必须与之匹配,以便该值通过约束验证。它必须是一个有效的JavaScript正则表达式,如RegExp类型所使用的,并在我们的正则表达式指南中记录。

如果未指定的pattern或其值无效,则不应用任何正则表达式,并且该属性将被忽略。

如果<input>控件中包含pattern属性,则应该在该控件附近提供可见的上下文信息来描述该pattern所对应的功能。此外,还要包含一个title属性,用于描述pattern。用户代理可以在约束验证期间使用title内容来告诉用户填入的内容和控件所需要的内容不匹配。一些浏览器显示带有title内容的工具提示,提高了视力正常用户的可用性。此外,辅助技术可以在控件获得焦点时大声朗读出title中内容。

<input type="text" id="uname" name="name" required size="45" pattern="[a-z]{4,8}" title="4 to 8 lowercase letters" />
User Name

当用户输入的信息没有匹配到pattern的约束时,就会显示title中提示信息:

正如上面示例你所看到的,pattern可以用来对<input>控件做一些简单的验证。除此之外,它在表单的可访问性方面也能起较大的作用。

<input>显式设置了pattern属性时,如果使用title属性,则必须描述pattern。对于文本内容的可视显示,通常不鼓励使用title属性,因为许多用户代理没有以可访问的方式公开该属性。有些浏览器在显示带有title的元素时会以提示框模式向用户呈现:

只不过这种提示信息的模式忽略了只依赖键盘和只有触摸的用户。因此,我们应该考虑比title更适合向用户描述pattern作用的方式。比如:

<label for="password">密码:</label>
<input type="password" id="password" name="password" required placeholder=" " pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}" title="你的密码必须至少包含6个字符,并包含至少一个大写字母、一个小写字母和一个数字" />
<div class="requirements">你的密码必须至少包含6个字符,并包含至少一个大写字母、一个小写字母和一个数字</div>
验证密码是否符合pattern规则
你的密码必须至少包含6个字符,并包含至少一个大写字母、一个小写字母和一个数字

简单地说,在<inpnut>pattern来对输入框做验证,最为主要的是pattern的值,即对应的正则表达式。

// URL
const regex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/

regex.test('https://www.w3cplus.com') // ❯❯❯ true
regex.test('htts://www.w3cplus.com')  // ❯❯❯ false

上面的正则,可以直接用到pattern属性中:

<input type="url" title="请输入正确的URL网址" name="url" id="url" placeholder="https://www.w3cplus.com" required  pattern="/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/" />
url验证
请输入正确的URL网址

如果你对正则熟悉,那么这一切都是简单的事情。下面有一些常见的正则表达方式:

// 密码(大写、小写、数字)
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$

// 密码(大写、小写、数字、特殊字符和最小8个字符)
(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$

// 美国运通信用卡
[0-9]{4} *[0-9]{6} *[0-9]{5}

// 信用卡号码
[0-9]{13,16}

// 银联卡
^62[0-5]\d{13,16}$

// Visa
^4[0-9]{12}(?:[0-9]{3})?$

// 万事达
^5[1-5][0-9]{14}$

// 手机号码
^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$

// 身份证
^([0-9]){7,18}(x|X)?$

// 日期: DD.MM.YYYY
(0[1-9]|1[0-9]|2[0-9]|3[01]).(0[1-9]|1[012]).[0-9]{4}

// 日期: YYYY-MM-DD
[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])

// 日期:YYYY-MM-DD
(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))

// 日期: MM/DD/YYYY
(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d
// 或
(?:(?:0[1-9]|1[0-2])[\/\\-. ]?(?:0[1-9]|[12][0-9])|(?:(?:0[13-9]|1[0-2])[\/\\-. ]?30)|(?:(?:0[13578]|1[02])[\/\\-. ]?31))[\/\\-. ]?(?:19|20)[0-9]{2}

// 时间:HH:SS
(([0-1]\d)|(2[0-3])) :[0-5]\d

// Email
\w+@\w+\.\w+


// 没有空格,没有特殊字符
[A-Za-z0-9]{5,50}

如果你对这方面感兴趣的话,还可以花点时间阅读:

CSS美化<input>

由于<input>控件类型众多,有些控件我们是可以很好的使用CSS直接美化,以达到所需要的UI风格,但其中有些控件是CSS控制起来是较为困难的,比如typecheckboxradiofilerangenumber以及日期类型等。而且不同的设备平台以及系统对靜单控件的渲染(UI)都会有所差异。接下来,我们来看看怎么通过CSS来给<input>控件添加样式,从而达到我们所需要的UI效果。

appearance

appearance属性非常有意思,它允许开发者根据操作系统的主题将(几乎)任何元素显示为平台原生样式,并允许使用none值删除任何平台带有的原生样式。

比如说,我们可以使用:

div {
    appearance: radio
}

让一个div看起来像一个单选按钮,但一般情况之下请不要这样做。

如果希望使用CSS来对原生控件实现一些自定义UI效果,那么appearance取值就非常有意义,即取值none时允许开发者删除平台对表单控件自带的原有样式,可以使用CSS来重新设置它样的样式

使用伪类选择器来美化<input>

在前面聊<input>的属性时,有些属性会给<input>元素带来对应状态相匹配的伪类选择器,而这些伪类选择器可以很好的帮助我们来对<input>的不同状态添加UI样式。

伪类选择器 描述 备注
:enalbed 适用于在具有disabled属性的<input>控件在未显式设置disabled状态下 控件可操作
:disabled 适用于在具有disabled属性的<input>控件在显式设置disabled状态下 控件不可操作
:read-only 适用于在具有readonly属性的<input>控件上显式设置readonly状态下 控件不可编辑,输入,但可获得焦点
:read-write 适用于在具有readonly属性的<input>控件未显式设置readonly状态下 控件可编辑,可输入
:default 表示只处于默认状态的表单元素。匹配在页面加载或渲染时选中的复选框或单选按钮 也适用于<select><option>
:checked 匹配当前选中的复选框和单选按钮的<input> 也适用于<select><option>
:indeterminate 可应用于状态不确定的表单元素 使用JavaScript给checkbox设置indeterminate的值为true,表单中拥有相同name值的所有radio都未被选中时
:placeholder-shown 适用于可以显式设置placeholder属性的<input>控件,并显式设置了该属性值 <input>得到焦点,placholder值失效,对应的:placeholder-shown也将失效
:valid 用来校验用户在<input>控件中输入的内容,表示输入的内容符合验证条件 符合校验条件样式
:invalid 用来校验用户在<input>控件中输入的内容,表示输入的内容不符合验证条件 不符合校验条件样式
:user-invalid 类似:invalid,但在blur上激活。匹配无效输入,但仅在用户交互之后匹配 只是改进了伪类:invalid
:in-range 其当前值处于minmax限定的范围之内 适用于可设置minmax<input>控件
:out-of-range 其当前值处于minmax限定的范围之外 适用于可设置minmax<input>控件
:required 可运用于显式设置了required属性的<input> 显式设置了required属性,用户未输入内容时会被触发
:optional 适用于未显式设置required属性的<input>控件 允许表单容易的展示可选字段并且渲染其外观
:blank 匹配没有输入值的<input>控件  

开发者可以根据<input>控件对应的伪类选择器在相应的属性下提供相应的CSS代码,为<input>控件在相应的状态下提供UI效果:

很多时候,我们可能结合CSS的其他选择器,比如:not()~+等表单控件样式更为灵活。而且还可以将上面提到的多个伪类选择器结合在一起:

input[type="text"]:invalid:not(:focus):not(:placeholder-shown) {
    background-color: pink
}

input[type="text"]:invalid:not(:focus):not(:placeholder-shown) + label  {
    opacity: 0
}

有关于这方面更多的介绍,还可以阅读:

你可能已经在自己的项目中使用纯CSS来对一些<input>控件提供更好的UI风格,比如文本输入框复选框单选按钮按钮等。但有一些<input>控件仅仅用我们上面聊到的或者你所掌握的常规CSS规则是较难满足的。其实我们还有很一些特殊技巧,可以让我们来针对一些特殊的<input>控件。这里不会非常详细的介绍每一个步骤和环节,但会告诉大家如何来给这些控件设计CSS。

有些<input>控件有着自己私有的伪元素,这些伪元素有些是浏览器客户端私有的,并且不同的控件有不同的伪元素。可能不太了解哪些控件有哪些私有的伪元素,但不用着急,我们可以借助浏览器的调试器来查阅。

这些私有的伪元素一般是在用户代理的Shadow DOM中显示

如果你使用的是Chrome浏览器的话,你可以选在调试器的配置项中,开启Shadow DOM选项:

这样就可以在对应的DOM元素上查看到相应的隐藏伪元素:

这样我们就可以使用像::-webkit-color-swatch-wrapper::-webkit-color-swatch伪元素设备type="color"<input>样式。注意不同的<input>不一样,比如type="datetime-local"就要比type="color"的复杂多了:

如果你使用Firefox浏览器的话,可以像下面这样开启Shadow DOM的显式。在Firefox浏览器的URL地址栏中输入about:config,然后搜索devtools.inspector.showAllAnonymousContent,并将其设置为true

同样的方式,你可以看到相应的<input>控件具有的私有伪元素:

同样是type="color",在Chrome和Firefox浏览器下就有所差异:

这样我们就可以来对<input>控件添加个性化的样式了(自定义表单控件样式)。就拿type="color"为例吧。

<input type="color" value="#ff3366" />

Chrome和Firefox的效果就不一样:

不同浏览器客户端默认渲染type="color"就有所差异:

我们重置input的样式:

input[type="color"] {
    appearance: none;
    width: 20vmin;
    height: 20vmin;
    border-radius: 50%;
    border: none 0;
    padding: 0;
    margin: 0;
}

你将看到的效果如下:

中间颜色块并没有像我们想象的那样,也变成一个圆,现在存在这样的现象主要是Shadow DOM中div引起的:

<!-- Chrome -->
<div pseudo="-webkit-color-swatch" style="background-color: rgb(255, 51, 102);"></div>

<!-- Firefox -->
<div style="background-color:#ff3366"></div>

// Chrome CSS
input[type="color" i]::-webkit-color-swatch {
    background-color: rgb(0, 0, 0);
    min-width: 0px;
    -webkit-user-modify: read-only !important;
    border-width: 1px;
    border-style: solid;
    border-color: rgb(119, 119, 119);
    border-image: initial;
    flex: 1 1 0%;
}

// Firefox CSS
input[type="color"]::-moz-color-swatch {
    width: 100%;
    height: 100%;
    min-width: 3px;
    min-height: 3px;
    margin-inline-start: auto;
    margin-inline-end: auto;
    box-sizing: border-box;
    border: 1px solid grey;
    display: block;
}

针对不同浏览器自己私有的伪元素做样式上的调整:

input[type="color"]::-moz-color-swatch {
    border-radius: 50%;
    border: none 0;
}

input[type="color" i]::-webkit-color-swatch-wrapper {
    padding: 0;
}

input[type="color" i]::-webkit-color-swatch {
    border-radius: 50%;
    border: none 0;
}

你现在看到的效果就统一起来了:

如果你要是基于这个,就可以做一些特别的效果处理,比如@Noah Blon在CodenPen上写的一个效果,结合CSS的mix-blend-mode可以对小汽车的图片做一些特殊效果处理:

不同的<input>对应的私有伪元素更详细的清单,可以点击这里查阅

基于这个方法,我们就可以对其他的表单控件,或者说其他的UI控件,按这样的方式做样式上的处理。如果你感兴趣的话,不仿自己尝试一下,要是你对这方面更详细的内容感兴趣的话,可以阅读下面这些文章:

改变光标颜色(caret-color)

有的时候,可能会有需求改变<input>控件在输入时或获得焦点时,想调整控件中光标的颜色:

在CSS中,可以使用caret-color属性来调整<input><textarea>控件的光标颜色,该属性还适用于显式设置了contenteditable属性的其他HTML元素:

input, 
textarea, 
[contenteditable] { 
    color: #495057; /* 文本颜色 */ 
    caret-color: red; /* 光标颜色 */ 
}

如果你希望不兼容的浏览器也能有效果,可以像下面这样来写代码:

input, 
textarea, 
[contenteditable] { 
    color: red; 
    text-shadow: 0px 0px 0px #495057; 
    -webkit-text-fill-color: transparent; 
} 

@supports (caret-color: red) { 
    input, 
    textarea, 
    [contenteditable] { 
        color: #495057; /* 文本颜色 */ 
        caret-color: red; /* 光标颜色 */ 
    } 
} 

具体效果如下:

有关于caret-color的介绍可以阅读《如何改变表单控件光标颜色》一文。

表单验证

用户填好表单之后提交都会对已提交的数据进行验证。在Web中,表单数据的验证常见的方式主要有客户端校验服务端校验。在这里我们主要来简单的聊一下客户端方面的表单校验。

客户端的校验是指发生在浏览器端,表单数据被提交到服务器之前,这种方式相较于服务端校验来说,用户体验会更好,它能实时的反馈用户的输入校验结果,这种类型的校验可以进一步细分为:

  • JavaScript内置的校验,这是可以完全自定认的实现方式
  • HTML内置校验,这不需要任何的JavaScript脚本代码,而且性能更好,但是不能像JavaScript那样可自定义

HTML内置校验

对于<input>控件的校验,其自身有很多属性就可以用来校验表单数据。换句话说,这些属性可以用来简单的校验用户所填的数据是否符合控件的数据要求。常用来校验<input>的属性有:

  • required属性,表示该控件是必填项,如果用户未填就数据就是无效,反之就是有效
  • pattern属性,它的值(正则表达式)常用来校验用户输入的数据是否符合正则表达式所要求的格式,如果符合就是有效的,反之是无效的
  • minmax属性,对于有区域范围的<input>(比如range),可以用来校验用户输入的数据是符在这个区域内,如果在这个区域内是有效的,反之是无效的
  • maxlengthminlength属性,可以用来校验用户输入的字符数,当用户输入的字符数小于minlength或大小maxlength指定的值都是无效的,反之是有效的

对于表单数据校验的结果是否通过校验,我们可以使用一些伪类选择器在样式上做差异化处理,这样可以第一时间告诉用户自己所填的数据是否符合表单控件的数据格式和要求。

  • 使用:required:optional来区别必填项和非必填项差异,显式设置了required的控件会匹配:required伪类选择器指定样式
  • 使用:in-range:out-of-range来区别区域内和区域外的样式,如果用户所填数据在符合区域内(min ~ max)则会使用:in-range伪类选择器指定的样式,反之会使用:out-of-range伪类选择器指定的样式
  • :valid:invalid用来指定有效和无效时对应的样式

注意,有些同学会将:required:invalid认为是等效的,实际上并非如此。前者是必填时触发的,后者是无效时触发的。比如说,当一个显式设置了required<input>控件,用户未填入数据时就提交,这个时候就触发了:required伪类指定的样式,而这个时候也会被认为是无效,则也触发了:invalid伪类选择指定的样式。

上面示例你可能已经发现了,前两个<input>我们显式设置了required属性,但这种体验是不太好的:显式设置了required属性是必需的,但最初是个空值,所以页面加载的时候它是无效的,则会触发:invalid伪类选择器指定的样式。这会让用户感到困惑。

我不没填内容,怎么就发生了错误呢?

针对这样的场景,@Dave Rupert在《Happier HTML5 Form Validation》一文中提供了相应的解决方案。就在提交事件之前,浏览器执行一个form.checkValidity()检查,它检查所有<input>。所有带有无效数据折<input>将触发invalid事件,然后对应的添加error类名:

const inputs = document.querySelectorAll("input");

inputs.forEach(input => {
    input.addEventListener(
        "invalid",
        event => {
            input.classList.add("error");
        },
        false
    );
});

input.error {
    border-left-color: salmon;
    color: salmon;
}

使用JavaScript校验

如果你想控制原生错误信息的界面外观,或者你想处理不支持HTML内置表单校验的浏览器,则必须使用 Javascript。

约束校验的API及属性主要有:

属性 描述
validationMessage 一个本地化消息,描述元素不满足校验条件时(如果有的话)的文本信息。如果元素无需校验(willValidatefalse),或元素的值满足校验条件时,为空字符串
validity 一个 ValidityState 对象,描述元素的验证状态
validity.customError 如果元素设置了自定义错误,返回 true ;否则返回false
validity.patternMismatch 如果元素的值不匹配所设置的正则表达式,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.rangeOverflow 如果元素的值高于所设置的最大值,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.rangeUnderflow 如果元素的值低于所设置的最小值,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.stepMismatch 如果元素的值不符合 step 属性的规则,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.tooLong 如果元素的值超过所设置的最大长度,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.typeMismatch 如果元素的值出现语法错误,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
validity.valid 如果元素的值不存在校验问题,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :valid CSS 伪类,否则命中 :invalid CSS 伪类
validity.valueMissing 如果元素设置了 required 属性且值为空,返回 true,否则返回 false。当此属性为 true 时,元素将命中 :invalid CSS 伪类
willValidate 如果元素在表单提交时将被校验,返回 true,否则返回 false

约束校验API的方法:

方法 描述
checkValidity() 如果元素的值不存在校验问题,返回 true,否则返回 false``。如果元素校验失败,此方法会触发invalid 事件
HTMLFormElement.reportValidity() 如果元素或它的子元素控件符合校验的限制,返回 true 。 当返回为 false 时, 对每个无效元素可撤销 invalid 事件会被唤起并且校验错误会报告给用户
setCustomValidity(message) 为元素添加一个自定义的错误消息;如果设置了自定义错误消息,该元素被认为是无效的,则显示指定的错误。这允许你使用 JavaScript 代码来建立校验失败,而不是用标准约束校验 API 所提供的。这些自定义信息将在向用户报告错误时显示。如果参数为空,则清空自定义错误

有关于使用JavaScript相关API对表单验证的教程可以阅读:

<input>的方法

<input>控件有一些自带的方法,这些方法主要由HTMLInputElement接口提供,该接口表示DOM中的<input>元素。除此之外,还可以使用父接口HTMLElementElementNodeEventTarget

其中HTMLInputElement接口提供了特定的属性和方法(继承自常规的HTML元素接口),用于管理<input>元素的布局和外观:

有关于HTMLInputEelement接口提供的属性和方法,更详细的介绍可以查阅读MDN提供的相关文档。这里主要和大家简单的聊一下<input>常见的几个方法,即focusblurclickchangeinput等。

focusblur

<input>是一个可聚焦元素,用户点击<input>元素或使用键盘上的Tab键选中<input>时,该元素将会获得焦点(focus)。如果<input>元素显式设置了autofocus(注意,一个页面只允许一个元素有该属性),那么页面一加载该元素就会获得焦点。

聚焦到一个元素通常意味着:

准备在此接受数据

所以,这正是我们可以运行代码以初始化所需功能的时刻。

失去焦点的时刻(blur)可能更为重要。它可能发生在用户点击页面其他地方,或者按下Tab键跳转到下一个表单字段(或可聚焦元素),亦或是其它途径的时候。失去焦点通常意味着:

数据已经输入完成

所以,我们可以运行代码来检查它,甚至可以将其保存到服务器上,或进行其他操作。

对于<input>元素,当其获得焦点时会触发focus事件,反之,失去焦点时会触发blur事件。

input.addEventListener("blur", function () {
    if (!input.value.includes("@")) {
        // not email
        input.classList.add("invalid");
        help.innerHTML = "Please enter a correct email.";
    }
});

input.addEventListener("focus", function () {
    if (this.classList.contains("invalid")) {
        // 移除 "error" 指示,因为用户想要重新输入一些内容
        this.classList.remove("invalid");
        error.innerHTML = "";
    }
});

上面的示例中:

  • blur事件处理程序检查这个字段是否输入了电子邮箱,如果没有输入,会添加类名invalid类名,并且显示一个错误信息
  • focus事件处理程序会隐藏错误信息,并将<input>上的invalid类名移除

有一点需要注意,我们无法在blur事件中调用event.preventDefault()来“阻止失去焦点”,因为blur事件是在元素失去焦点之后运行的

还有,focusblur事件不会向上冒泡,但可以使用focusinfocusout事件(和focusblur事件完全一样),它们会冒泡,不过focusinfocusout只能使用addEventListener来分配,并不能使用类似onfocusonblur

对于focusblur,简单的总结一下:

它们不会冒泡。但是可以改为捕获阶段触发,或者使用focusinfocusout

change

<input>元素更改完时(失去焦点),将触发change事件。比如,当我们在<input>中键入内容时,并不会触发change事件,但我们将焦点移出<input>到其他位置时,就会触发change事件。

对于typecheckboxradio<input>,会在选项更改后立即触发change事件。

input

每当用户对<input>的值(value)进行修改后,就会触发input事件。

与键盘事件不,只要值改变了,input事件就会触发,即使那些不涉及键盘行为的值的更改也是如此。

input事件是处理对<input>的每次更改的最佳方式。

另外,<input>触发input事件,我们同样无法使用event.preventDefault()(因为太迟了,不会起任何作用)。

click

click可以用来模拟鼠标左键单击一个元素。当在支持click的元素上使用该方法时(比如<input>元素),会触发该元素的click事件。该事件会一直向文档的上层元素冒泡,也会触发它们各自的click事件。

HTMLInputElement涉及较多内容,如果你想对这方面进一步了解,可以阅读有关于HTMLInputElement相关的知识

可访问性

构建一个具有可访问性和可用性表单是一个复杂的体系,这里面将会涉及到较多的知识体系。比如我们前面已经了解到的:

  • 选择正确的type值或inputmode,这样可以给用户提供更好的输入方式(特别是在移动端,可以唤起正确的软键盘,提供最佳的输入方式)
  • 使用正确的属性值,比如disabled(告诉用户不能操作)、readonly(告诉用户只能阅读)、required(告诉用户必填项)、checked(告诉用户已选中)等
  • 使用正确的伪类选择器,给用户提供更好的视觉
  • 使用自带的验证属性,在表单提交的时候能第一时间告诉用户输入的信息是否符合要求

除了上述提到的之外,还有很多其他的细节,可以帮助我们更好的构建可访问性的表单:

  • 不要使用placeholder替代<label>标签
  • 不要仅用颜色来区分其状态
  • 使用正确的ARIA属性

这里无法一下将有关于可访问性方面介绍清楚,如果你对这方面感兴趣的话,欢迎观注A11Y系列的更新,在后面会专门花一篇文章来介绍这方面。如果你要是迫不及待的话可以先阅读下面相关教程:

如果你Web可访问性(Web无障碍设计)方面感兴趣的话,可以阅读小站上有关于A11Y系列文章

小结

文章开头就提到过,在Web中构建表单是件复杂的事情,构建一个具有可访问或可用的表单更不是件易事。在表单中有很多个表单元素组成,其中就离不开我们今天聊的<input>元素。这篇文章主要介绍了<input>元素的类型(type),基本属性、方法等,特别是在现代Web开发中,我们可以借助元素自身带有的伪类选择器对表单进行UI上的美化,如果结合一些自带的验证属性,更能第一时间给用户反馈。当然,要构建可访问性的表单,这些仅仅是最基础的部分,换句话说,要构建一个较好的可访问性表单,将会涉及很多知识体系,因此才会说不是件易事。

最后希望这篇文章能给大家有所帮助。如果你在这方面有更多的经验或建议,欢迎在下面的评论中与我们一起分享。

返回顶部