配了三年下拉菜单,今天才发现 CSS 自己会认「开着」这个状态了——:open 把这件事彻底原生化了
以前配下拉菜单的展开态,每次都要写两套样式然后靠 JS 切——今天 CSS 自己会认「开着」这个状态了,:open 把这件事彻底原生化了。
<details> 展开时什么样式,<select> 点开时什么样式,dialog 打开时什么样式——以前全要靠classList.toggle或者[open]属性选出来再加 class,CSS 本身只管得了「隐藏」,管不了「当前是开着还是关着」这个语义状态。
这就是 :open 要解决的问题。
一行替代三年的 JS
:open是 CSS Selectors Level 4 里的伪类,专门选「当前处于打开状态」的元素。支持的元素有三个:details、select(下拉选择器)、input[type="date"]这类带 picker 的输入元素。
/* 以前:靠 JS 加 class */
details.is-open .content { ... }
select.is-open { ... }
/* 现在:浏览器自己认状态 */
details:open .content { ... }
select:open { ... }
浏览器自己维护打开/关闭状态,CSS 直接读这个状态,不用再绕 JS。
三种场景直接跑
场景一:details 展开样式
details {
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s;
}
details:open {
border-color: #3b82f6;
box-shadow: 0 4px 12px rgb(59 130 246 / 0.15);
}
details:open summary {
color: #3b82f6;
font-weight: 600;
}
不用 JS 改 class,details 的开闭状态 CSS 自己读得到。展开时边框变蓝,加阴影,summary 文字变色,全是纯 CSS。
场景二:select 展开换箭头
select {
appearance: none;
background: white url("data:image/svg+xml,...") no-repeat right 12px center / 16px;
padding: 10px 40px 10px 14px;
border: 1px solid #d1d5db;
border-radius: 6px;
}
select:open {
border-color: #6366f1;
background-color: #f5f3ff;
background-image: url("data:image/svg+xml,...");
outline: 2px solid rgb(99 102 241 / 0.2);
outline-offset: 1px;
}
点开时背景变紫,箭头朝上,蓝色边框 outline。旧写法要监听focus事件改 class,现在直接:open一步到位。
场景三:父容器跟着子元素状态变
这是最实用的一类——子元素打开了,父容器也要跟着变。以前靠 JS 读子元素状态改父 class,现在靠:has()组合:open:
.select-wrapper:has(select:open) {
border-color: #6366f1;
box-shadow: 0 0 0 3px rgb(99 102 241 / 0.15);
}
这个组合直接说「当 wrapper 里的 select 处于打开状态时,wrapper 本身也变样式」。没有 JS,没有 class 切换,就一行选择器。
这个思路是更大的趋势
:open不是孤立的案例。它是 CSS 近年来「把状态判断收归浏览器」这条主线的其中一环:
:popover-open——popover 显示时:modal——dialog 以 modal 方式打开时@position-try——弹层被屏幕边缘截断时自动换向scroll-state()——滚动到了哪一段
这些以前全要靠 JS 监听+计算+改 class,现在浏览器自己算,CSS 读原生状态就行。前端写的 JS 少了,出 bug 的机会也少了。
浏览器支持与落地方案
主流浏览器全部支持(Chrome 113+、Safari 17.5+、Firefox 121+),Baseline 2026 已标注为「广泛可用」,生产环境直接用。
如果要同时适配开和关两种状态,用:not(:open):
select:not(:open) { /* 默认态 */ }
select:open { /* 展开态 */ }
下一步可以做的
把手里一个用 JS 写了下拉/折叠态的组件拿出来,把classList.add/remove的逻辑删掉,换成:open和:has()。先跑通,再看能不能把 JS 完全卸掉——有些项目做到最后,状态管理的 JS 全部删光了,就剩 HTML 和 CSS。
评论区
登录后可评论。