ElementInternals.states 是只读的 CustomStateSet,仅用于向 CSS 伪类和辅助技术暴露状态;必须通过 setValidity() 设置,不可直接增删查改,且变更非即时生效。

ElementInternals.states 是一个 CustomStateSet 实例,但它**不能直接赋值或手动构造**——你只能通过 ElementInternals.setValidity() 或原生表单约束 API 间接影响它;想“管理组件内部状态”时,误以为能像 Map 一样增删查改 states,基本会踩坑。
为什么不能直接操作 states?
states 是只读属性,返回的 CustomStateSet 对象没有 add()、delete()、has() 等方法(目前所有浏览器实现中均为空壳对象)。它的存在仅用于向辅助技术(AT)和伪类(如 :state(loading))暴露状态,实际状态变更必须走标准路径:
-
setValidity({ customError: true }, "invalid-format")→ 触发:state(invalid-format) - 调用
reportValidity()或触发原生验证流程后,对应 state 才会被 AT 识别 - 没有声明
formAssociated: true的自定义元素,internals.states始终为空且不可用
setValidity() 是唯一可靠入口
真正能写入 states 的只有 ElementInternals.setValidity(),且仅支持预定义的验证状态键(valueMissing、typeMismatch 等),以及通过 customError + 第二个参数注入自定义 state 名:
class MyInput extends HTMLElement {
constructor() {
super();
this.internals = this.attachInternals();
}
set loading(value) {
if (value) {
// ✅ 正确:触发 :state(loading) 伪类
this.internals.setValidity(
{ customError: true },
"loading"
);
} else {
// ✅ 必须重置为有效,否则 state 持续存在
this.internals.setValidity({});
}
}
}
注意:"loading" 不是任意字符串——它需满足 CSS 标准对 :state(...) 的命名要求(仅含字母、数字、连字符,且不以数字开头)。
立即学习“前端免费学习笔记(深入)”;
常见错误:混淆 states 和普通状态管理
开发者常试图用 states 替代组件内部的 this._loading = true,这是错的:
-
states不参与 JS 逻辑,无法在代码里if (this.internals.states.has("loading")) - 它不触发事件、不更新属性、不响应式,纯粹是给 CSS 和 AT 用的“信号灯”
- 若需要 JS 可读的状态,请单独维护布尔变量或
Map,再同步调用setValidity()
真正要小心的是:states 的变更不是即时生效的——它依赖浏览器对表单验证周期的调度,比如 reportValidity() 被调用、或用户交互触发验证时,才可能被渲染引擎采纳。别指望设完立刻看到 :state(loading) 匹配上样式。



















