必须同时满足三个条件:static formAssociated = true 声明、构造函数调用 this.attachInternals()、shadow DOM 中存在已挂载且可聚焦的内部元素(如<input>),否则浏览器不识别为表单控件,FormData 无法获取值,验证和聚焦均失效。

不能只设 static formAssociated = true 就完事——浏览器根本不会把它当表单控件用,FormData 拿不到值,form.elements 里查不到,提交时也完全无视它。
必须调用 attachInternals() 并绑定内部可聚焦元素
表单关联自定义元素(FACE)要真正被识别,得满足三个硬性条件:
-
static formAssociated = true是声明,不是开关;没它直接被忽略 - 构造函数里必须调用
this.attachInternals(),否则this.internals是undefined - shadow DOM 里必须有一个真实挂载的、可聚焦的子元素(如
<input>或<textarea>),且该元素需在connectedCallback后已存在于 DOM 中
常见错误:在 constructor 里就调 this.internals.setValidity(),但 shadowRoot 还没 append,anchor 元素为 null,导致后续验证失败时抛出 An invalid form control is not focusable。
name 属性必须透传到 internals 和内部控件
原生表单靠 name 收集数据,FACE 必须把自身 name 显式同步给内部控件和 internals:
立即学习“前端免费学习笔记(深入)”;
- 自定义元素自身要有
nameattribute(用户写<my-input name="email"></my-input>) - 在
attributeChangedCallback('name', _, newValue)里,把newValue同时设给内部<input>的name,并调用this.internals.setFormValue(newValue ? new FormData() : null)(若需参与提交) - 不设
name→FormData里字段为空 → 后端收不到这个字段
注意:this.internals.setFormValue() 接收的是 FormData 实例或 null,不能传字符串——传字符串会导致 TypeError: Failed to execute 'setFormValue' on 'ElementInternals'。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
提交时数据怎么进 FormData?得手动桥接
FACE 不会自动把内部控件的值塞进表单数据,你得自己搭这座桥:
- 监听内部
<input>的input或change事件 - 事件回调中构建
FormData,例如:const fd = new FormData(); fd.append(this.name, this.input.value); - 调用
this.internals.setFormValue(fd)(注意不是setValue) - 如果控件支持多值(如带
multiple的<select>),得用fd.append()多次,不能只用fd.set()
漏掉这步,表单提交时这个自定义元素的值永远是空的,哪怕界面上明明填了内容。
验证锚点(anchor)不设等于埋雷
当调用 form.reportValidity() 或点击 submit 按钮触发校验时,浏览器会尝试聚焦首个无效控件。如果 FACE 的 setValidity() 没传第三个参数(anchor),就会报错:
An invalid form control with name='email' is not focusable.
- anchor 必须是内部一个已挂载、可聚焦、且
delegatesFocus: true的元素(比如<input>) - 不能传
this、this.shadowRoot或未插入 DOM 的节点 - 推荐写法:
this.internals.setValidity({ valueMissing: true }, '邮箱不能为空', this.input),其中this.input是对内部<input>的引用
这个 anchor 不仅影响报错,还决定用户按 Tab 键时焦点是否能自然落到你的自定义控件上——很多人卡在这一步,却以为是 shadow DOM 阻断了聚焦逻辑。


















