hidden 属性不能作为状态机的状态,因为它仅是视图层副作用,无状态记忆、转换规则或事件响应能力;必须由 javascript 主动管理状态流转,再用 hidden 同步 dom 显隐。

Hidden 属性不能直接驱动状态机,它只是状态变更后的副作用呈现方式之一;你必须先用 JS 实现状态流转逻辑,再把 hidden 当作“视图层输出”来同步。
为什么不能拿 hidden 当状态机的“状态”本身
HTML 的 hidden 是纯视图控制属性,没有内置状态记忆、转换规则或事件响应机制。它不参与任何状态定义(比如 “loading” → “success”)、不触发状态迁移钩子、也不支持 guard 条件判断。把它当状态变量用,就像用 style.display 存储用户登录态一样——能凑合显示,但逻辑会迅速失控。
常见错误现象:
- 在 Web Component 中只监听
el.hasAttribute('hidden')判断当前“状态”,结果初始 HTML 带hidden时返回true,JS 后续设el.hidden = false后仍读到true(因为 attribute 没删干净) - 用 CSS 强制覆盖
div[hidden] { display: block !important; },导致hidden失效却没意识到状态和 UI 已脱节 - 多个同级元素靠
hidden控制显隐,但没统一状态源,导致 A 显示时 B 还没隐藏,出现视觉重叠
正确做法:用 Map + switch 管理状态,hidden 仅用于 DOM 同步
把状态定义收口到一个 JS 对象里,每个状态值对应一组 DOM 元素的 hidden 设置。不要让 DOM 属性反向决定状态。
示例结构:
const stateMap = new Map([
['idle', { '#loading': true, '#content': true, '#error': true }],
['loading', { '#loading': false, '#content': true, '#error': true }],
['success', { '#loading': true, '#content': false, '#error': true }],
['error', { '#loading': true, '#content': true, '#error': false }]
]);
function setState(newState) {
const rules = stateMap.get(newState);
if (!rules) return;
Object.entries(rules).forEach(([selector, shouldHide]) => {
const el = document.querySelector(selector);
if (el) el.hidden = shouldHide;
});
}
关键点:
- 状态迁移由 JS 主动调用
setState('loading')触发,不是监听 DOM 变化 -
hidden赋值用el.hidden = true/false,别用setAttribute,避免 attribute 和 property 不一致 - 所有要控制显隐的元素必须有唯一可选中的
id或data-state-target,否则规则无法精准映射
Web Component 内部状态同步时的典型陷阱
自定义组件中,常把内部 <input type="checkbox"> 的 checked 和外部 hidden 混为一谈。其实二者完全无关——hidden 控制的是整个组件容器是否渲染,而 checked 是子控件的业务状态。
容易踩的坑:
- 在
attributeChangedCallback里看到hidden变了,就去改this.#input.checked = true——这是错的,hidden变化只表示组件被隐藏/显示,不代表用户点了开关 - 组件
unmounted时只清空setTimeout,却忘了设this.#contentEl.hidden = true,导致卸载后 DOM 仍残留可见内容 - 服务端直出 HTML 时写了
<my-component hidden></my-component>,但 JS 初始化时没检查this.hidden初始值,直接设state = 'idle',造成首次渲染闪动
性能与可访问性必须兼顾的边界情况
hidden 的语义是“当前不相关”,所以它天然跳过屏幕阅读器。但如果你的状态机里有“加载中”状态,只靠 hidden 控制提示文案,就会让视障用户完全感知不到加载进度。
此时必须额外加 aria-live 或 aria-busy:
<div id="loading" hidden aria-live="polite">正在加载...</div> <div id="content" hidden aria-busy="false"></div>
注意:
-
hidden和aria-*必须同步更新,不能只改一个 - 动画过渡不能依赖
hidden切换,它不触发 CSS transition;要用opacity+pointer-events配合hidden的移除时机 - SSR 渲染时若状态未确定(比如异步数据还没回来),宁可先不写
hidden,也不要硬塞一个假状态导致首屏错乱
真正难的从来不是怎么写 el.hidden = true,而是想清楚:这个 true 是谁决定的、在什么时机决定、以及当它变成 false 时,有没有其他地方还偷偷依赖着旧的 true。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











