1. 简介
本节为非规范性内容。
自动填充是 Web 的一项关键功能,每天为数百万用户减少操作阻力。 它广泛用于登录界面、电子商务和联系表单等场景。 尤其对于商务和结账流程,自动填充能为买家体验和商家成果带来显著益处。
与此同时,Web 上的自动填充存在若干缺陷:填充不完整或仅部分填充、 跨浏览器互操作性问题,以及开发者实现和维护成本高昂。
一个关键示例是地址自动填充,正确实现时,它是一种动态表单: 不同地理区域对地址输入具有不同的结构和要求。选择 国家/地区需要更改表单(重新排列字段、添加和移除字段),并依赖 用户的输入,但 自动填充会介入此交互,并且可能无法正确响应 所呈现的表单。
当前的“行业标准”解决方案需要使用隐藏表单字段,尝试预先判断并捕获 正确的信息,然后再将其呈现给用户。该解决方案脆弱且 复杂。更糟的是,它巩固了使用隐藏字段支持正当用例的做法,但相同的 技术也可能且确实会被恶意行为者滥用。
本规范引入了一个 AutofillEvent,
该事件会在自动填充值提交到
表单字段之前触发,从而允许开发者:
-
检查即将自动填充的值
-
根据这些值动态调整表单(例如,显示特定国家/地区的地址字段)
-
在表单准备好接受自动填充值时向用户代理发出信号
1.1. 目标
-
用户:提高自动填充的可靠性,并为未来可能 弃用填充隐藏字段铺平道路
-
网站开发者:降低使用隐藏字段处理地址自动填充所需的复杂性, 并减少对特定实现技巧的需求。
-
自动填充提供方:为第三方自动填充提供方提供可与 新 API 无缝协作的挂钩
1.2. 示例
< form id = "checkout" > < input autocomplete = "name" placeholder = "全名" > < input autocomplete = "street-address" placeholder = "街道地址" > < input autocomplete = "address-level2" placeholder = "城市" > < input autocomplete = "postal-code" placeholder = "邮政编码" > < input autocomplete = "country" placeholder = "国家/地区" > <!-- 对于需要州/省字段的国家/地区,将动态添加该字段 --> </ form > < script > document. addEventListener( 'autofill' , async function ( event) { // 在自动填充值中查找国家/地区值 let countryValue= null ; let formElement= null ; // 查找国家/地区元素和值 for ( const [ element, value] of event. autofillValues) { if ( element. autocomplete=== 'country' ) { countryValue= value; formElement= element. form; break ; } } // 如果填充美国地址,则需要添加州选择器 if ( event. refill!== null ) { if ( countryValue=== 'US' ) { // 检查是否已经存在州字段 const existingState= formElement. querySelector( '[autocomplete="address-level1"]' ); if ( ! existingState) { // 为美国地址创建并插入州选择器 const stateSelect= document. createElement( 'select' ); stateSelect. autocomplete= 'address-level1' ; stateSelect. name= 'state' ; stateSelect. innerHTML= ` <option value="">选择州……</option> <option value="AL">阿拉巴马州</option> <option value="AK">阿拉斯加州</option> <option value="AZ">亚利桑那州</option> <option value="CA">加利福尼亚州</option> <option value="CO">科罗拉多州</option> <!-- ……其他州…… --> <option value="WY">怀俄明州</option> ` ; // 插入到邮政编码字段之前 const postalCode= formElement. querySelector( '[autocomplete="postal-code"]' ); postalCode. parentNode. insertBefore( stateSelect, postalCode); // 表明表单已被修改,应重新执行自动填充 await event. refill(); } } else if ( countryValue=== 'UK' ) { ... 添加英国- 特定逻辑} } else { // 用户代理不支持重新填充。从隐藏字段中提取值, // 或通知用户需要手动填写这些值。 } }); </ script >
autofillValues
属性返回一个自动填充值条目列表,其中每个
条目都是由目标 HTMLElement
和要填充的值组成的元组。开发者可以
遍历这些条目以检查待处理的自动填充数据,并确定是否需要
调整表单。
表单结构更改后(例如,为添加特定国家/地区的字段,可能以异步方式进行),
开发者调用 refill。
这会向用户代理表明表单已被
修改,并且应使用更新后的表单结构重试自动填充操作。
注意:refill
属性在事件第二次分派时为 null
(表单修改后),以防止无限循环。
2. 概念
2.1. 自动填充值条目
自动填充值 条目是由以下内容组成的元组:
-
一个
HTMLElement——将接收自动填充值的表单控件 -
一个
DOMString——要从用户的自动填充配置文件中填入的值
用户代理会根据控件的
autocomplete
属性将表单控件与自动填充数据进行匹配(参见 [HTML] 中的自动填充字段名称)。
它还可以使用由实现定义的启发式方法。
2.2. 重新填充操作
重新填充操作 允许开发者表明表单结构已响应自动填充值而 被修改,并且用户代理应再次尝试填充 表单。
3. AutofillEvent 接口
[Exposed =Window ]interface :AutofillEvent Event {(constructor DOMString ,type optional AutofillEventInit = {});eventInitDict readonly attribute FrozenArray <AutofillValueEntry >autofillValues ;readonly attribute RefillCallback ?refill ; };callback =RefillCallback Promise <undefined > ();dictionary :AutofillEventInit EventInit {sequence <AutofillValueEntry >= [];autofillValues boolean =allowRefill true ; };typedef sequence <any >; // AutofillValueEntry 是由 [HTMLElement, DOMString] 组成的元组 // 其中第一个元素是表单控件,第二个元素是要填充的值AutofillValueEntry
AutofillEvent
接口表示一个事件,该事件会在用户代理即将对表单字段执行分派时,
对其进行自动填充。
3.1. 属性
autofillValues 属性返回一个
自动填充值
条目列表。每个条目都是一个元组,其中第一个元素是 HTMLElement
(要填充的表单控件),第二个元素是 DOMString
(要填充的值)。
refill 属性返回一个 RefillCallback
或
null。当其不为 null 时,调用此回调会返回一个 Promise,
等待该 Promise 时,会向用户代理表明
表单结构已被修改,应重试自动填充。
在以下情况下,refill
属性为 null:
-
该事件是在先前调用
refill后作为重试而分派的;或者 -
用户代理确定重新填充不适用于此自动填充操作。
这可以防止页面持续修改表单并请求重新填充而导致无限循环。
每个 AutofillEvent
都有一个关联的 自动填充值列表
(一个由自动填充值
条目组成的列表),初始为空列表。
每个 AutofillEvent
都有一个关联的 允许重新填充标志
(一个布尔值),
初始为 true。
每个 AutofillEvent
都有一个关联的 分派时间戳
(一个 DOMHighResTimeStamp),
初始为 0。
每个 AutofillEvent
都有一个关联的 重新填充待处理标志
(一个布尔值),
初始为 false。
4. 处理模型
4.1. 触发自动填充事件
注意:自动填充操作会在事件分派后立即执行。
refill
回调允许页面在修改表单结构后,
在一个由实现定义的超时窗口内请求额外执行一次自动填充。
4.2. 处理重新填充请求
AutofillEvent
event,要处理重新填充
请求:
-
令 now 为当前高精度时间。
-
令 elapsed 为 now 减去 event 的分派时间戳。
-
令 refillTimeout 为一段由实现定义的持续时间。
-
如果 elapsed 大于 refillTimeout,则返回一个以“
InvalidStateError”DOMException拒绝的 Promise。 -
如果 event 的允许重新填充标志为 false,则返回一个以“
InvalidStateError”DOMException拒绝的 Promise。 -
如果 event 的重新填充待处理标志为 true,则返回一个以“
InvalidStateError”DOMException拒绝的 Promise。 -
将 event 的重新填充待处理标志设置为 true。
-
令 promise 为一个新的 Promise。
-
令 document 为 event 的相关文档。
-
返回 promise,并并行地:
注意:超时时间是由实现定义的,以便用户代理能够灵活
平衡
响应速度与给予页面足够时间调用 refill。
用户代理
应选择能够提供良好用户体验的超时时间。
注意:在重试分派时(调用 refill
后),
refill
属性为 null,以防止无限循环。
4.3. 与 HTML 自动填集成
当用户代理的自动填充机制被触发时(例如,由用户与 自动填充 UI 交互而触发),并且用户选择了要填充的值,用户代理必须在将这些值 提交到表单字段之前触发自动填充事件。
5. “full-address”自动完成标记
本规范引入了一个新的自动填充字段名称:“full-address”。
当一个表单控件的 autocomplete
属性设置为“full-address”时,
用户代理应请求访问用户完整地址数据的权限,包括
当前表单中可能不存在的字段。
这使表单能够通过 AutofillEvent
接收全面的地址信息,从而允许表单动态调整其结构,以容纳
用户地址中的所有相关字段。
full-address 启用全面的地址自动填充:
< form autocomplete = "full-address" > < input name = "country" autocomplete = "country" > < div id = "dynamic-address-fields" ></ div > </ form >
6. 安全和隐私注意事项
AutofillEvent
会在自动填充值提交到表单字段之前将其暴露给 JavaScript。
用户代理应确保仅在用户明确同意自动填充后才触发该事件
(例如,从下拉列表中选择自动填充建议)。
传递给事件的数据仅限于用户代理打算填入页面表单中的数据, 因为该 API 的结构要求使用元素作为自动填充值的键。
请注意,当事件在调用 refill() 后触发时,表单可能包含
用户代理第一次填充表单时不存在的新字段。用户代理
在填充这些新表单字段之前仍应考虑用户同意,就像支持
自动重新填充的用户代理目前已经执行的那样。
6.1. 第三方自动填充提供方
浏览器扩展和第三方自动填充提供方(例如密码管理器)可以
通过构造并分派具有相同结构的 AutofillEvent
来使用此 API,
从而确保无论自动填充来源如何,行为都保持一致。