摘要
写页面时,经常碰到像下拉框、折叠面板这类能开能关的玩意儿。以前想给关上的状态写样式,得绕个弯子用:not(:open),读起来就跟念绕口令似的。最近CSS社区为这事儿吵得挺凶,琢磨着要不要加个:closed伪类。咱今儿就扒一扒这个:open是咋用的,再聊聊没有:closed的时候,怎么用:not(:open)把活儿干漂亮,顺便看看这哥们儿到底该不该有。
啥是:open
:open这玩意儿,就是专门用来逮那些能打开、能关上的元素,并且是在它们处于“打开”状态的时候下手。好比说那个能点开收起的<details>标签,还有那个下拉菜单<select>,只要它们处在展开或者弹出的状态,:open就能精准命中。
/* 给打开的详情区域换个皮肤 */
details:open {
background: #f0f0f0;
border-left: 4px solid #06c;
}这段代码一上,只要<details>展开,背景立马变灰,左边还带条蓝边,跟装了感应器似的。不过得留个心眼,这玩意儿刚出来那会儿,Safari浏览器还没跟上,所以写样式的时候最好先测测,别翻车。
用:open搞定弹出层样式
实际操作里头,:open最常用来对付那些临时蹦出来的家伙。比如做一个自定义的下拉选择框,点开的时候得给点特殊待遇。
<select class="fancy-select">
<option>摸鱼模式</option>
<option>爆肝模式</option>
<option>躺平模式</option>
</select>.fancy-select:open {
border-color: #f90;
box-shadow: 0 0 0 3px rgba(255, 153, 0, 0.25);
background-color: #fff9e6;
}这么一搞,下拉框打开的时候,边框变橙黄,还带一圈光晕,背景也跟着暖起来,用户一下就知道这玩意儿能选东西。不过有个小坑,:open只管“打开”那一刻的状态,要是想给“关上”的时候也搞点花样,就得另寻出路。
没有:closed的日子,:not(:open)来顶班
既然现在还没个:closed能用,那想摸到关上的元素,就得靠:not(:open)这个反向操作。别看它多写几个字,用熟了也挺顺手。
流程一:给关上的折叠面板加个暗淡效果
有时候希望那个<details>收起来的时候,文字颜色淡一点,显得不那么起眼,等展开了再精神起来。这活儿就得:not(:open)来干。
- 搭个基础架子:先弄个普通的
<details>框,里头放点内容。
<details class="info-card">
<summary>点击瞅瞅干货</summary>
<p>里面全是硬核内容,平时藏着掖着。</p>
</details>- 写样式先管展开的:按照正常逻辑,把展开时的样式写出来,比如边框、背景、字体都整精神点。
.info-card:open {
border: 2px solid #06c;
background: #e6f4ff;
padding: 1rem;
}- 再写关上的样式:用
:not(:open)把关上的状态逮住,让它看起来蔫儿一点。
.info-card:not(:open) {
opacity: 0.7;
filter: grayscale(0.2);
}这么一来,页面上一排折叠面板,没打开的都灰扑扑的,一眼就能看出哪个是收着的哪个是展开的。写这步的时候有个关键点,:not(:open)会匹配所有不是打开状态的东西,包括那些压根不能开关的元素。所以最好给目标元素加个类名,比如.info-card,精准定位,别误伤友军。
流程二:给自定义选择器整点关闭提醒
做个表单的时候,下拉框没选东西之前,也就是关闭状态,可以加个淡入淡出的提示,让用户知道该点它了。
- HTML结构:一个普通的
<select>,配个提醒的文案。
<div class="select-wrapper">
<select class="theme-select">
<option value="">-- 选个皮肤 --</option>
<option>暗黑</option>
<option>明亮</option>
</select>
<span class="hint">点我选皮肤呀~</span>
</div>- 隐藏提示,等关闭时再出现:正常情况下,让提示藏起来,等下拉框关上(也就是没打开)的时候,再让它幽幽地显示。
.hint {
display: none;
font-size: 0.8rem;
color: #999;
margin-top: 0.25rem;
}
.theme-select:not(:open) ~ .hint {
display: block;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}效果就是,下拉框一关上,底下就冒出一行小字“点我选皮肤呀~”,一点开就消失。这样做用户体验感拉满,比干巴巴地杵在那儿强多了。不过得注意,:not(:open)在<select>上的支持,不同浏览器可能有点时间差,写代码前最好翻翻最新的兼容性表格。
江湖传说:closed到底该不该有
说到这儿,肯定有人嘀咕,搞这么复杂,直接来个:closed不就完事了?其实这事儿在CSS社区里吵了两年多。2022年那会儿,大伙儿刚开始琢磨:open,后来有人一拍大腿,觉得有开就得有关,对称才美。但一深入讨论,问题来了,如果有了:closed,那像<div>这种永远关不上的元素,:closed到底算不算它?算的话逻辑就乱了,不算的话,又得在规范里给它开小灶。
那帮搞标准的也是头大,最后2024年底暂时拍板,先只整:open,:closed的事儿搁置。但同时也透了个口风,未来有可能改。所以目前最稳当的玩法,还是用:not(:open)先顶着。这俩写法在逻辑上完全等价,就像问“你不是那个意思?”跟“你是另一个意思?”在语义上有点微妙差别,但最终指向一样。
其实用:not(:open)还有个隐藏好处,那就是强迫自己写选择器的时候,必须明确知道在选啥。比如想给所有不是打开状态的<details>加样式,就得写details:not(:open),脑子里自然就会过滤掉那些无关元素。要是真来个:closed,可能一不留神就写个*:closed,那乐子就大了,页面里所有不能开关的东西全被套上样式,直接翻车。
