配了三年交互状态,今天 CSS 终于把「打开还是关闭」这件事原生化了——:open 伪类把打开状态彻底从 JS 里拆出来了
你每次写展开面板、下拉菜单、对话框的时候,第一步是不是先在 JS 里写 isOpen = !isOpen,然后再给元素挂一个 .is-open 或者 [data-state="open"] 的 class?
这个流程,今天被 CSS 彻底接管了。
:open 是什么
CSS Selectors Level 4 定义了一个 :open 伪类,它匹配所有「当前处于打开状态」的元素,覆盖这些 HTML 场景:
<details open>— 折叠面板<dialog>— 对话框(无论通过.showModal()还是.show()打开)<select>— 下拉选择框(打开时匹配)<input type="date">、<input type="time">等日期/时间选择器- 所有带
open属性的可切换元素
一个属性,把语义状态直接暴露给 CSS,不需要任何 JS 介入。
和 :popover-open 不是一回事
2026 年 8 月 Baseline 的 :open 容易和 :popover-open 混淆,后者是专门给 <popover> 元素准备的。区别在于:
:open覆盖<details>、<dialog>、<select>、日期选择器等原生可折叠元素:popover-open只匹配<popover>属性元素的「显示状态」
两者并列存在,对应不同的语义树。
5 行代码把展开面板的样式写完
用 <details> + :open 做一个带动画的折叠面板:
/* 默认收起状态:箭头向右 */
summary::after {
content: "→";
transition: transform 0.2s;
}
/* 展开状态:箭头向下,背景变浅蓝 */
details[open] {
background: #f0f7ff;
border-color: #3b82f6;
}
details[open] summary::after {
transform: rotate(90deg);
}
/* 父容器在展开时加高亮 */
details:has([open]) {
box-shadow: 0 2px 12px rgba(59, 130, 246, 0.2);
}
HTML:
<details>
<summary>点击展开更多设置</summary>
<div class="settings-panel">
<label><input type="checkbox"> 启用通知</label>
<label><input type="checkbox"> 暗黑模式</label>
</div>
</details>
不需要 is-open class,不需要 JS 监听 toggle 事件,CSS 自己读元素的打开状态。
<dialog> 也有了独立的状态选择器
对话框以前只能靠 JS 的 .showModal() 和 .close() 驱动样式,现在 CSS 可以直接追踪:
/* 对话框打开时,背景加模糊遮罩 */
dialog[open] {
opacity: 1;
transform: scale(1);
}
/* 或者用 :open 更通用(dialog 打开时隐式带 open 属性) */
dialog:open {
background: white;
border-radius: 12px;
}
注意:dialog 元素在 .show() 调用时会隐式添加 open 属性,.close() 时移除。所以 :open 对 <dialog> 天然生效,不需要在 HTML 里写 open。
为什么比 JS 方案更合理
传统 JS 方案的流程是:用户点击 → JS 修改变量 → JS 更新 DOM class → CSS 读取 class 改变样式。这条链路上,任何一步出错都会导致「点了没反应」或者「样式对不上」。
:open 的流程是:用户点击 → 浏览器更新元素状态 → CSS 直接读取浏览器管理的状态。没有中间变量,没有 class 同步,没有 race condition。
更重要的是,这是语义级别的状态。:open 匹配的是「元素当前是否展开」这个语义事实,而不是「某个变量是不是 true」。屏幕阅读器、搜索引擎爬虫早就把这些元素的 open 属性当作语义信号来处理,现在 CSS 也在用同一套信号。
浏览器支持情况
截至 2026 年 8 月,:open 在以下版本稳定支持:
- Chrome 122+(2024年3月)
- Firefox 128+(2024年7月)
- Safari 17.2+(2024年1月)
- Edge 122+(2024年3月)
覆盖率达到 Baseline「广泛可用」标准,即所有主流浏览器在所有活跃版本均已支持。对于现代项目,可以直接使用,无需 @supports 降级。
下一步:把状态逻辑搬回 CSS
如果你的项目里还有「点击展开 → JS 切换 class → CSS 读 class 变样式」这套流程,今天就是迁移的时机:
- 把
is-openclass 换成原生<details>或<dialog> - 把
[data-state="open"]选择器换成:open - 删除 JS 里的
classList.toggle和状态变量 - 用
:has(:open)处理父容器联动
结果:代码更短,逻辑更干净,语义更正确。
结语: 浏览器在 2026 年把「交互状态」这件事彻底还给了平台。:open 让 CSS 直接读语义状态,不需要 JS 做中间人。这不是 CSS 在抢 JS 的活——是 CSS 在做它本来就该做的事:给元素的语义状态找到对应的样式表达。
评论区
登录后可评论。