You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

表单关联自定义元素(FACE)验证异常:无效控件无法聚焦求助

解决Form-Associated Custom Element表单验证时的「不可聚焦」报错问题

你遇到的「An invalid form control with name='foobar' is not focusable.」报错,核心原因是:浏览器在表单验证失败时,会自动尝试聚焦第一个无效控件,但Form-Associated Custom Element(FACE)默认不属于原生可聚焦元素,也没有内置聚焦逻辑,导致浏览器无法完成聚焦操作,抛出错误。

下面是具体的解决方案,结合代码示例说明:

关键修复步骤

1. 让自定义元素成为可聚焦元素

通过给自定义元素添加tabindex属性,使其具备可聚焦能力。推荐设为-1,这样元素不会被Tab键遍历到,但仍能通过代码触发聚焦。

2. 实现聚焦转发逻辑

当浏览器尝试聚焦自定义元素时,将焦点转发到内部第一个未通过验证的输入框,既满足浏览器的聚焦要求,又能让用户直观看到哪个输入框有问题。

3. 重写验证相关方法

FACE需要实现checkValidity()和reportValidity()方法,同步内部输入框的验证状态,让表单能正确识别自定义元素的有效性。

4. 同步表单值

通过attachInternals()提供的setFormValue()方法,将内部输入框的值组合后同步到表单,确保提交时能获取到自定义元素的数据。

完整修复代码示例

class DualInputField extends HTMLElement {
  // 声明为表单关联元素
  static formAssociated = true;

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    // 渲染内部两个输入框
    this.shadowRoot.innerHTML = `
      <div>
        <label>输入1(必填):<input type="text" id="input1" required></label>
        <label>输入2(必填):<input type="text" id="input2" required></label>
      </div>
    `;
    // 关联表单内部实例
    this.internals = this.attachInternals();
    // 设置tabindex让元素可聚焦
    this.setAttribute('tabindex', '-1');
    // 获取内部输入框引用
    this.input1 = this.shadowRoot.getElementById('input1');
    this.input2 = this.shadowRoot.getElementById('input2');
  }

  // 重写focus方法,转发焦点到第一个无效输入框
  focus() {
    const invalidInput = this.input1.validity.valid ? this.input2 : this.input1;
    invalidInput.focus();
  }

  // 重写checkValidity,返回内部输入框的整体验证状态
  checkValidity() {
    return this.input1.checkValidity() && this.input2.checkValidity();
  }

  // 重写reportValidity,触发内部无效输入框的原生验证提示
  reportValidity() {
    const isValid = this.checkValidity();
    if (!isValid) {
      const invalidInput = this.input1.validity.valid ? this.input2 : this.input1;
      invalidInput.reportValidity();
    }
    return isValid;
  }

  // 监听输入事件,同步表单值
  connectedCallback() {
    this.input1.addEventListener('input', () => this.#syncFormValue());
    this.input2.addEventListener('input', () => this.#syncFormValue());
    this.#syncFormValue();
  }

  // 私有方法:同步内部输入值到表单
  #syncFormValue() {
    this.internals.setFormValue(`${this.input1.value}|${this.input2.value}`);
  }
}

// 注册自定义元素
customElements.define('dual-input-field', DualInputField);

表单使用示例

<form id="demoForm">
  <dual-input-field name="foobar"></dual-input-field>
  <button type="submit">提交表单</button>
</form>

<script>
document.getElementById('demoForm').addEventListener('submit', (e) => {
  e.preventDefault();
  // 触发表单整体验证
  if (e.target.checkValidity()) {
    const formData = new FormData(e.target);
    console.log('验证通过,表单数据:', formData.get('foobar'));
  }
});
</script>

额外说明

  • 如果需要让自定义元素可以通过Tab键访问,可将tabindex设为0,同时记得处理blur等事件保持逻辑一致性。
  • 若内部输入框有自定义验证规则,可在checkValidity()中添加对应的校验逻辑,确保整体验证状态准确。

内容的提问来源于stack exchange,提问作者Mark Tyers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:35:03