你以为播放器样式只能靠 JS 监听?今天 CSS 七族伪类把这件事彻底原生化了
配了五年播放器,每次想让视频播的时候样式跟着变,都要在 JS 里写 play/pause 事件监听,classList.add/remove 跑一套——现在 CSS 七族媒体状态伪类把这件事彻底原生化了。
播放器状态一变,样式就得跟着变,这件事前端工程师没少做。传统做法是 JS 监听事件,手动 toggle class,再在 CSS 里写 .is-playing/.is-paused 样式。
一个播放心视频要写多少?先绑 play 事件,再绑 pause 事件,可能还要管 ended、waiting、volumechange 事件。每加一个播放器就得复制粘贴一套。这还是只有「播放/暂停」两个状态的简单场景。
Chrome 152 稳定版带来了完整的媒体元素状态伪类家族:
/* 正在播放(未暂停、未结束) */
video:playing { }
/* 暂停中(含初始未播放状态) */
video:paused { }
/* 跳转/拖动进度条时 */
video:seeking { }
/* 正在缓冲(网络卡住) */
video:buffering { }
/* 缓冲失败/卡死 */
video:stalled { }
/* 静音状态 */
video:muted { }
/* 音量被系统锁定(如 iOS) */
video:volume-locked { }
浏览器自动维护这些状态,CSS 负责样式,不用写一行 JS。
实战一:播放按钮浮层
想让视频暂停时显示一个大大的播放图标:
.player video:paused::after {
content: "▶";
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-size: 3rem;
background: rgb(0 0 0 / 0.4);
color: white;
pointer-events: none;
}
video:playing::after { display: none; }
三行 CSS,浏览器自动判断显示/隐藏,播放器切到播片状态图标就消失。
实战二:静音图标联动
用相邻兄弟选择器,纯 CSS 让音量图标跟着视频静音状态走:
.volume-icon::before { content: "🔊"; }
video:muted ~ .volume-icon::before { content: "🔇"; }
不用 JS 写事件监听,CSS 直接读媒体元素状态。
实战三:缓冲指示器动画
卡顿时自动出现脉冲动画:
@keyframes pulse {
50% { opacity: 0.5; }
}
.record-indicator { display: none; }
video:buffering ~ .record-indicator {
display: block;
animation: pulse 1s ease-in-out infinite;
}
用户网络一卡,CSS 自动切到缓冲状态,动画就起来了。
浏览器支持现状
| 伪类 | Chrome | Firefox | Safari |
|---|---|---|---|
| :playing | 152 | 150 | 15.4 |
| :paused | 152 | 150 | 15.4 |
| :muted | 152 | 150 | 15.4 |
| :buffering | 152 | 150 | × |
| :seeking | 152 | 150 | × |
| :stalled | 152 | 150 | × |
| :volume-locked | 152 | 150 | × |
Safari 支持 :playing、:paused、:muted 三个,:buffering 等较新的还在跟进。
渐进增强写法
@supports (selector(video:playing)) {
video:paused::after { display: grid; }
} @supports not (selector(video:playing)) {
.is-paused::after { display: grid; }
}
先写伪类方案,Safari 用 @supports not 兜底 JS class 方案。
三个坑
一、:paused 会匹配初始未播放状态。海报图 + 播放按钮的场景里,页面刚加载时视频就是 :paused,所以播放按钮浮层的设计要区分「未播放」和「用户主动暂停」两种场景——前者通常不需要显示暂停图标。
二、Safari 暂不支持 :buffering / :seeking。这两个较新,全量支持要等到 Safari 跟进。用 @supports (selector(video:buffering)) 做条件加载,别一股脑全上。
三、:volume-locked 只在移动端有意义。桌面浏览器音量不会被系统锁定,这个伪类始终不匹配,给桌面写样式时别指望它。
三个下一步
一、找一个现有播放器的 HTML,把 JS 里的 play/pause 事件监听换成 CSS 伪类,验证行为是否一致。
二、给缓冲指示器写一个 CSS 动画,搭配 :buffering 伪类,做一个网络卡顿时自动出现的「加载中」效果。
三、用 @supports selector(video:playing) 做渐进增强,让 Safari 用户走 JS class 兜底方案,其他用户走纯 CSS。
评论区
登录后可评论。