HTML5 数据列表实现轻量级自动完成控件

在本教程中,我们将深入探讨HTML5中鲜为人知的<datalist>元素。它能够实现一个轻量级、可访问且无需JavaScript的跨浏览器自动完成表单控件。

为何<select>不够理想?

HTML5的<select>控件在用户需从有限选项中选择时非常理想。但当遇到以下情况时,它们就不那么实用了:

  • 选项众多,例如国家或职位头衔
  • 用户希望输入列表中未包含的自定义选项

显而易见的解决方案是自动完成控件。这允许用户输入几个字符,从而快速筛选出可用选项以供选择。

<select>在你开始输入时会跳转到正确的位置,但这并不总是显而易见的。它并非在所有设备上都有效(例如触摸屏),且会在一两秒内重置。

开发者常常求助于众多JavaScript驱动解决方案,但自定义自动完成控件并非总是必需。HTML5的<datalist>元素轻量、可访问,且无JavaScript依赖。你可能听说过它存在缺陷或支持不足。这在2021年已非事实,但确实存在浏览器兼容性问题和注意事项。

<datalist> 快速入门

从包含200多个选项的列表中选择国家,是自动完成控件的理想应用场景。在HTML页面中直接定义一个<datalist>,其子元素为<option>,每个国家对应一个选项:

<datalist id="countrydata">
  <option>Afghanistan</option>
  <option>Åland Islands</option>
  <option>Albania</option>
  <option>Algeria</option>
  <option>American Samoa</option>
  <option>Andorra</option>
  <option>Angola</option>
  <option>Anguilla</option>
  <option>Antarctica</option>
  ...etc...
</datalist>

然后,任何<input>字段都可以通过list属性引用该datalist的id

<label for="country">country</label>

<input type="text"
  list="countrydata"
  id="country" name="country"
  size="50"
  autocomplete="off" />

值得注意的是,最好设置autocomplete="off"。这样可以确保用户看到<datalist>中的值,而不是他们在浏览器中以前输入的值。

结果展示:

这是Microsoft Edge的默认渲染效果。其他应用程序也实现了类似功能,但界面在不同平台和浏览器上有所差异。

<option> 选项设置

通常,使用<option>的文本子元素作为标签:

<datalist id="mylist">
  <option>label one</option>
  <option>label two</option>
  <option>label three</option>
</datalist>

使用value属性也能达到相同效果:

<datalist id="mylist">
  <option value="label one" />
  <option value="label two" />
  <option value="label three" />
</datalist>

注意:在HTML5中,结束的/>斜杠是可选的,尽管它可能有助于防止编码错误。

你还可以根据选定的标签设置值,以下是两种格式:

选项1:

<datalist id="mylist">
  <option value="1">label one</option>
  <option value="2">label two</option>
  <option value="3">label three</option>
</datalist>

选项2:

<datalist id="mylist">
  <option value="1" label="label one" />
  <option value="2" label="label two" />
  <option value="3" label="label three" />
</datalist>

在这两种情况下,当选择有效选项时,输入字段会设置为123,但不同浏览器中的用户界面可能有所不同。

  • Chrome显示的列表中同时包含值和标签,但一旦选项被选中,仅保留值。
  • Firefox显示的列表只包含标签,选中选项后切换显示对应的值。
  • Edge则仅显示值。

以下CodePen示例展示了所有这些变化:

查看Pen
HTML5 <datalist> 自动完成示例
by SitePoint (@SitePoint)
on CodePen.

实现方式可能会演变,但目前建议不要使用值和标签,因为这可能会让用户感到困惑。(下面讨论了一个解决方案.)

<datalist> 浏览器支持和回退方案

<datalist> 元素在现代浏览器以及Internet Explorer 10和11中得到良好支持:

尽管存在一些实现细节,但它们不会影响大多数使用场景。最坏的情况是,输入框可能会退回到标准文本输入模式。

如果必须支持IE9及以下版本,可以使用一种回退模式,即当<datalist>失败时,结合标准<select>与文本输入。以国家选择为例进行调整:

<label for="country">country</label>

<datalist id="countrydata">

  <select name="countryselect">
    <option></option>
    <option>Afghanistan</option>
    <option>Åland Islands</option>
    <option>Albania</option>
    <option>Algeria</option>
    <option>American Samoa</option>
    <option>Andorra</option>
    <option>Angola</option>
    <option>Anguilla</option>
    <option>Antarctica</option>
    ...etc...
  </select>

  <label for="country">or other</label>

</datalist>

<input type="text"
  id="country" name="country"
  size="50"
  list="countrydata"
  autocomplete="off" />

查看SitePoint(@SitePoint)在CodePen上的Pen
HTML5 <datalist> 自动完成回退

在现代浏览器中,<option>元素成为<datalist>的一部分,且“或其他”标签不会显示。它与上面的例子看起来相同,但countryselect表单值将被设置为空字符串。

在IE9及以下版本中,长长的<select>和文本输入字段都是激活的:

在旧版IE中,两个值都可以输入。你的应用程序必须决定:

  • 哪个更有效,或者
  • 使用一个小型JavaScript函数,当另一个改变时重置其中一个

在非文本控件上使用<datalist>

基于Chrome的浏览器也可以将<datalist>值应用于:

  1. 类型为"date"的输入。用户可以从定义为YYYY-MM-DD值的一系列选项中选择,但以本地格式呈现。

  2. 一种类型为 "color" 的输入。用户可以从定义为六位十六进制值的颜色选项中选择(三位数值不适用)。

  3. 一种类型为 "range" 的输入。滑块显示刻度标记,但这并不限制可输入的值。

查看Pen
HTML5 <datalist> 应用于其他输入类型
由 SitePoint (@SitePoint)
CodePen 上创建。

<datalist> CSS 样式

如果你曾为样式化一个 <select> 框而苦恼,… 那你还算幸运!

一个 <input> 可以像常规元素一样进行样式化,但与之关联的 <datalist> 及其子元素 <option> 无法通过CSS进行样式化。列表的呈现完全由平台和浏览器决定。

I hope this situation improves, but for now, a solution is proposed at MDN which:

  1. 覆盖了浏览器的默认行为
  2. 实际上将 <datalist> 视为 <div>,以便通过CSS进行样式化
  3. 使用JavaScript复制了所有自动完成功能

I’ve enhanced it further and the code is available on GitHub. To use it, load the script anywhere in your HTML page as an ES6 module. The jsDelivr CDN URL can be used:

<script src="https://cdn.jsdelivr.net/npm/datalist-css/dist/datalist-css.min.js"></script>

或者,如果您使用打包器,可以通过npm安装:

npm install datalist-css

您的<datalist>元素必须采用<option>value</option>格式。例如:

<datalist id="mylist">
  <option>label one</option>
  <option>label two</option>
  <option>label three</option>
</datalist>

注意:<option value="value" />不能使用,因为它会导致无法设置样式的空元素!

随后,可以添加CSS来为部分或全部<datalist><option>元素设置样式。例如:

datalist {
  position: absolute;
  max-height: 20em;
  border: 0 none;
  overflow-x: hidden;
  overflow-y: auto;
}

datalist option {
  font-size: 0.8em;
  padding: 0.3em 1em;
  background-color: #ccc;
  cursor: pointer;
}

/* 活动选项样式 */
datalist option:hover, datalist option:focus {
  color: #fff;
  background-color: #036;
  outline: 0 none;
}

示例:

查看Pen
HTML5 <datalist>自动补全CSS样式
by SitePoint (@SitePoint)
on CodePen.

虽然样式化有效,但值得这么做吗?我怀疑未必……

  • 重新实现浏览器的标准键盘、鼠标和触摸控制,并保持合理的可访问性,是困难的。MDN示例不支持键盘事件,尽管我试图改进它,但在某些设备上仍不可避免地会出现问题。
  • 您依赖200行JavaScript来解决一个CSS问题。它最小化到1.5kB,但如果页面需要许多长的<datalist>元素,可能会引入性能问题。
  • 如果JavaScript是必需的,那么使用一个更美观、更一致、经过实战检验的JavaScript组件是否更为理想?

当JavaScript失效时,控件会回退到未加样式的标准HTML5 `<datalist>`,但这只是一个次要优势。

构建一个通过Ajax增强的`<datalist>`

假设设计师能接受浏览器样式差异,可以利用JavaScript增强标准`<datalist>`的功能,例如:

  1. 实现可选验证,仅允许在`<datalist>`中输入已知值。
  2. 通过Ajax调用搜索API返回的数据设置`<option>`元素。
  3. 当选择某个选项时,设置其他字段的值。例如,选择“美利坚合众国”会在隐藏输入中设置“US”。

代码主要需要重定义`<option>`元素,尽管存在多个编码考量:

  • Ajax API请求应在输入至少一定数量的字符后才触发。
  • 输入事件应进行防抖处理,即仅当用户停止输入至少半秒后才发起Ajax调用。
  • 查询结果应被缓存,以避免重复调用或解析相同请求。
  • 应避免不必要的查询。例如,输入“un”会返回12个国家。无需再为“unit”“united”发起额外的Ajax调用,因为所有相关选项均已包含在最初的12个结果中。

I’ve created a standard Web Component for this, and the code is available on GitHub. The CodePen example below allows you to select a valid country after entering at least two characters. A music artist autocomplete then returns artists who originated in that country with names matching the search string:

查看Pen
HTML5 <datalist> 结合Ajax自动完成功能
由SitePoint(@SitePoint
CodePen上提供。

要在自己的应用中使用,可将脚本作为ES6模块加载到HTML页面的任意位置。可使用jsDelivrCDN链接:

<script src="https://cdn.jsdelivr.net/npm/datalist-ajax/dist/datalist-ajax.min.js"></script>

若使用打包工具,亦可通过npm安装:

npm install datalist-ajax

创建一个<auto-complete>元素,其子元素<input>作为数据输入字段。例如,国家查询界面采用此方式:

<label for="country">country lookup:</label>

<auto-complete
  api="https://restcountries.eu/rest/v2/name/${country}?fields=name;alpha2Code;region"
  resultname="name"
  querymin="2"
  optionmax="50"
  valid="please select a valid country"
>
  <input type="text" id="country" name="country" size="50" required />
</auto-complete>

<auto-complete>元素属性包括:

attribute description
api the REST API URL (required)
resultdata the name of the property containing a result array of objects in the returned API JSON (not required if only results are returned)
resultname the name of the property in each result object which matches the search input and is used for datalist <option> elements (required)
querymin the minimum number of characters to enter before a search is triggered (default: 1)
inputdelay the minimum time to wait in milliseconds between keypresses before a search occurs (default debounce: 300)
optionmax the maximum number of autocomplete options to show (default: 20)
valid if set, this error message is shown when an invalid value is selected

REST URL 必须至少包含一个 ${id} 标识符,该标识符由具有该 id<input> 中设置的值替换。在上面的示例中,api URL 中的 ${country} 引用子 <input> 中的值,该子 <input>id"country"。URL 通常使用子输入,但页面上的任何其他字段都可以被引用。

restcountries.eu API 返回包含国家数据的单个对象或对象数组。例如:

[
  {
    "name": "Cyprus",
    "alpha2Code": "CY",
    "region": "Europe"
  },
  {
    "name": "Sao Tome and Principe",
    "alpha2Code": "ST",
    "region": "Africa"
  },
  {
    "name": "Andorra",
    "alpha2Code": "AD",
    "region": "Europe"
  }
]

resultdata 属性无需设置,因为这是返回的唯一数据(没有包装对象)。resultname 属性必须设置为 "name",因为该属性用于填充数据列表 <option> 元素。

选择选项时,其他字段可以自动填充。以下输入接收 "alpha2Code""region" 属性数据,因为已设置 data-autofill 属性:

<input data-autofill="alpha2Code" type="text" id="countrycode" name="countrycode" readonly />

<input data-autofill="region" type="text" id="region" name="region" readonly />

datalist-ajax 的工作原理

如果您不想阅读 230 行代码并保持魔法活力,可以跳过此部分!

代码最初在<auto-complete>内创建一个新的<datalist>,并通过list属性将其附加到子<input>上。一个input事件监听器监控<input>,当输入的字符达到最小数量且用户停止打字时,调用runQuery()函数。

runQuery()函数根据表单数据构建API URL,并使用Fetch API进行Ajax调用。返回的JSON数据被解析,然后构建一个包含<option>元素的可重用DOM片段,并将其放入缓存中。

A datalistUpdate() function is called, which updates the <datalist> with the appropriate cached DOM fragment. Further calls to runQuery() avoid Ajax calls if a query has already been cached or a previous query can be used.

A change event handler also monitors the <input>, which is triggered when focus is moved from the field and the value has been modified. The function checks that the value matches a known option and, if necessary, uses the Constraint Validation API to show the error message provided in the valid attribute.

假设选择了有效的选项,更改处理程序函数会填充所有带有匹配data-autofill属性的字段。保留对自动填充字段的引用,以便在输入无效选项后可以重置它们。

需要注意的是,未使用shadow DOM。这确保了自动完成<input>(及<datalist>)元素可以通过CSS进行样式设置,并在必要时被其他脚本访问。

Dunkin’ <datalist>

HTML5的<datalist>虽有限制,但如果你需要一个简单的框架无关自动完成字段,它是非常理想的。缺乏CSS支持令人遗憾,但浏览器供应商最终可能会解决这一疏忽。

本教程中展示的任何代码和示例均可用于你自己的项目。

HTML5数据列表元素常见问题解答

HTML5的数据列表元素是什么,它是如何工作的?

HTML5的数据列表元素是一个为输入元素预定义的选项列表。它为表单元素提供了“自动完成”功能。数据列表元素通过id属性与特定的输入元素关联。输入元素使用list属性来识别数据列表。在数据列表内部,你可以定义代表输入字段可用选项的选项元素。

如何使用HTML5的数据列表进行自动完成?

要使用HTML5的数据列表进行自动完成,你需要将数据列表与输入字段关联起来。这通过在输入字段中添加list属性并将其值设置为数据列表的id来实现。然后,浏览器将根据用户的输入和数据列表中定义的选项提供自动完成建议。

我能在所有浏览器中使用HTML5的数据列表元素吗?

HTML5的数据列表元素在大多数现代浏览器中都得到支持,包括Chrome、Firefox、Safari和Edge。然而,在Internet Explorer 9及更早版本中并不支持。你可以在如Can I Use这样的网站上查看当前的浏览器兼容性。

我如何对HTML5数据列表的选项进行样式设置?

遗憾的是,HTML5数据列表元素的样式选项相当有限。下拉列表的外观由浏览器控制,不能轻易地通过CSS进行自定义。不过,你可以对与数据列表关联的输入字段进行样式设置。

我能为单个输入字段使用多个数据列表吗?

不行,一个输入字段不能关联多个datalist。输入字段的list属性只能接受一个id,对应一个datalist。如果需要提供多组选项,可能需要使用JavaScript根据用户输入动态更改选项。

我可以在其他输入类型中使用HTML5的datalist吗?

可以,HTML5的datalist可以与多种输入类型配合使用,包括文本、搜索、网址、电话、电子邮件、日期、月份、周、时间、本地日期时间、数字、范围和颜色。不过,自动完成功能在某些输入类型(如范围或颜色)上可能无法按预期工作。

HTML5的datalist能与select元素一起使用吗?

不能,HTML5的datalist不能与select元素一起使用。datalist旨在为输入字段提供自动完成建议,而select元素提供了一个下拉选项列表。如果需要下拉列表,应使用select元素。

我可以在HTML5的datalist中使用JavaScript吗?

可以,你可以使用JavaScript与HTML5的datalist结合,动态添加、删除或更改选项。但请注意,datalist的选项不支持如onchange或onclick等事件。你需要在关联的输入字段上添加事件监听器。

HTML5的datalist能用于搜索字段吗?

可以,HTML5的datalist非常适合搜索字段。它可以根据用户输入提供自动完成建议,从而增强用户体验。但你需要手动填充datalist中的可能搜索词。

可以使用HTML5的datalist与textarea结合使用吗?

不可以,HTML5的datalist不能与textarea一起使用。datalist旨在为输入字段提供自动完成建议,而不是为textarea。如果需要在textarea中实现自动完成功能,可能需要使用JavaScript库或自行构建解决方案。

Source:
https://www.sitepoint.com/html5-datalist-autocomplete/