自动填充事件

社区组报告草案

本版本:
https://wicg.github.io/autofill-event/
问题跟踪:
GitHub
编辑:
Shopify

摘要

本规范定义了一个在用户代理即将自动填充表单字段时触发的事件,使开发者能够根据自动填充的值动态调整表单。

本文档的状态

本规范由 Web 平台孵化器 社区组发布。 它既不是 W3C 标准,也不处于 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议 (CLA) 可在有限范围内选择退出,并且还适用其他条件。 进一步了解 W3C 社区组和商业组

1. 简介

本节为非规范性内容。

自动填充是 Web 的一项关键功能,每天为数百万用户减少操作阻力。 它广泛用于登录界面、电子商务和联系表单等场景。 尤其对于商务和结账流程,自动填充能为买家体验和商家成果带来显著益处。

与此同时,Web 上的自动填充存在若干缺陷:填充不完整或仅部分填充、 跨浏览器互操作性问题,以及开发者实现和维护成本高昂。

一个关键示例是地址自动填充,正确实现时,它是一种动态表单: 不同地理区域对地址输入具有不同的结构和要求。选择 国家/地区需要更改表单(重新排列字段、添加和移除字段),并依赖 用户的输入,但 自动填充会介入此交互,并且可能无法正确响应 所呈现的表单。

当前的“行业标准”解决方案需要使用隐藏表单字段,尝试预先判断并捕获 正确的信息,然后再将其呈现给用户。该解决方案脆弱且 复杂。更糟的是,它巩固了使用隐藏字段支持正当用例的做法,但相同的 技术也可能且确实会被恶意行为者滥用。

本规范引入了一个 AutofillEvent, 该事件会在自动填充值提交到 表单字段之前触发,从而允许开发者:

  1. 检查即将自动填充的值

  2. 根据这些值动态调整表单(例如,显示特定国家/地区的地址字段)

  3. 在表单准备好接受自动填充值时向用户代理发出信号

1.1. 目标

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. 自动填充值条目

自动填充值 条目是由以下内容组成的元组:

  1. 一个 HTMLElement ——将接收自动填充值的表单控件

  2. 一个 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;
// AutofillValueEntry 是由 [HTMLElement, DOMString] 组成的元组
// 其中第一个元素是表单控件,第二个元素是要填充的值

AutofillEvent 接口表示一个事件,该事件会在用户代理即将对表单字段执行分派时, 对其进行自动填充

3.1. 属性

autofillValues 属性返回一个 自动填充值 条目列表。每个条目都是一个元组,其中第一个元素是 HTMLElement (要填充的表单控件),第二个元素是 DOMString (要填充的值)。

refill 属性返回一个 RefillCallbacknull。当其不为 null 时,调用此回调会返回一个 Promise, 等待该 Promise 时,会向用户代理表明 表单结构已被修改,应重试自动填充。

在以下情况下,refill 属性为 null

这可以防止页面持续修改表单并请求重新填充而导致无限循环。

每个 AutofillEvent 都有一个关联的 自动填充值列表 (一个由自动填充值 条目组成的列表),初始为空列表

每个 AutofillEvent 都有一个关联的 允许重新填充标志 (一个布尔值), 初始为 true。

每个 AutofillEvent 都有一个关联的 分派时间戳 (一个 DOMHighResTimeStamp), 初始为 0。

每个 AutofillEvent 都有一个关联的 重新填充待处理标志 (一个布尔值), 初始为 false。

4. 处理模型

4.1. 触发自动填充事件

给定一个文档 document、一个由自动填充值条目组成的列表 entries,以及一个布尔值 allowRefill,要触发自动填充 事件
  1. event 为使用 AutofillEvent 创建事件的结果。

  2. eventtype 属性初始化为“autofill”。

  3. eventbubbles 属性初始化为 true。

  4. eventcancelable 属性初始化为 false。

  5. event自动填充值列表设置为 entries

  6. event允许重新填充标志设置为 allowRefill

  7. event分派时间戳设置为当前高精度时间

  8. document分派 event

  9. 使用 entriesdocument 执行自动填充操作。

注意:自动填充操作会在事件分派后立即执行。 refill 回调允许页面在修改表单结构后, 在一个由实现定义的超时窗口内请求额外执行一次自动填充。

4.2. 处理重新填充请求

给定一个 AutofillEvent event,要处理重新填充 请求
  1. now当前高精度时间

  2. elapsednow 减去 event分派时间戳

  3. refillTimeout 为一段由实现定义的持续时间。

  4. 如果 elapsed 大于 refillTimeout,则返回一个InvalidStateErrorDOMException 拒绝的 Promise。

  5. 如果 event允许重新填充标志为 false,则返回一个InvalidStateErrorDOMException 拒绝的 Promise。

  6. 如果 event重新填充待处理标志为 true,则返回一个InvalidStateErrorDOMException 拒绝的 Promise。

  7. event重新填充待处理标志设置为 true。

  8. promise一个新的 Promise

  9. documentevent 的相关文档

  10. 返回 promise,并并行地

    1. entries 为更新后的自动填充值 条目(针对修改后的表单重新匹配用户的自动填充数据)。

    2. 将一个任务排入队列,以:

      1. 使用 documententries 和 false 触发自动填充事件

      2. 使用 entriesdocument 执行自动填充操作。

      3. 以 undefined 解决 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, 从而确保无论自动填充来源如何,行为都保持一致。

一致性

文档 约定

一致性要求通过描述性断言 与 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词“MUST”“MUST NOT”“REQUIRED”“SHALL”“SHALL NOT”“SHOULD”“SHOULD NOT”“RECOMMENDED” “MAY”和“OPTIONAL” 应按照 RFC 2119 中的描述进行解释。 但是,为了便于阅读, 本规范中的这些词并非全部使用大写字母。

本规范的所有文本均为规范性内容, 但明确标记为非规范性的章节、示例和注释除外。[RFC2119]

本规范中的示例以“例如”等词语引出, 或使用 class="example" 与规范性文本区分开来, 如下所示:

这是一个资料性示例。

资料性注释以“注意”一词开头, 并使用 class="note" 与规范性文本区分开来, 如下所示:

注意,这是一条资料性注释。

索引

本规范定义的 术语

通过引用定义的 术语

参考文献

规范性参考文献

[DOM]
Anne van Kesteren。DOM 标准。现行标准。 URL:https://dom.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss。高精度时间。URL:https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren;等。HTML 标准。 现行标准。URL:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。现行标准。URL:https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner。用于在 RFC 中 表示要求级别的关键词。1997年3月。当前最佳实践。URL:https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL 标准。现行 标准。URL:https://webidl.spec.whatwg.org/

IDL 索引

[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;
// AutofillValueEntry 是由 [HTMLElement, DOMString] 组成的元组
// 其中第一个元素是表单控件,第二个元素是要填充的值