你以为视频卡了只能靠JS监听waiting事件?今天CSS把媒体播放状态彻底原生化了
做播放器的都踩过这个坑——视频一卡,就要写一堆事件监听器去捕捉 waiting/playing/pause,还要手动维护一个状态变量,怕状态对不上。2026 年 8 月,CSS 终于把这件事彻底收编了:七个伪类,把你能想到的所有播放状态全包了,而且不需要一行 JavaScript。
哪七个伪类
:playing— 正在播放(包含因缓冲暂停但随时可恢复的情况):paused— 暂停:buffering— 正在缓冲(卡了,还没数据):seeking— 用户正在拖动进度条:stalled— 媒体加载卡死,没有任何数据进来:muted— 静音:volume-locked— 系统锁定了音量(比如手机响了)
旧写法 vs 新写法
旧写法(JS):
const video = document.querySelector("video");
video.addEventListener("playing", () => showPlayIcon());
video.addEventListener("waiting", () => showBuffering());
video.addEventListener("pause", () => showPauseIcon());
video.addEventListener("volumechange", () => updateMuteUI());
// 还要处理 edge case:buffering 期间 playing 仍然为 true
新写法(CSS):
video:playing { --play-state: playing; }
video:paused { --play-state: paused; }
video:buffering { --play-state: buffering; }
video:seeking { --play-state: seeking; }
video:stalled { --play-state: stalled; }
video:muted { --muted: true; }
配合一个状态指示器:
/* 一套 CSS,全链路覆盖,不用写任何 JS 状态管理 */
.status-indicator::before {
content: "▶"; /* 默认 */
}
video:paused .status-indicator::before { content: "⏸"; }
video:buffering .status-indicator::before { content: "⏳"; }
video:stalled .status-indicator::before { content: "⚠"; }
video:muted .status-indicator::after { content: "🔇"; }
一个细节:buffering 和 playing 同时生效
MDN 特别说明:视频在缓冲期间,:playing 仍然会匹配。两者不互斥——这意味着 CSS 里的层叠规则要设计好,否则冲突覆盖。
/* 先降权再高亮 buffer:优先级要处理好 */
video:buffering { outline: 3px solid orange; }
video:playing:not(:buffering) { outline: none; }
兼容性:Baseline 2026
- Chrome 152+ ✅
- Firefox 150+ ✅
- Safari 26+ ✅
- 全球覆盖率:~84%(2026 年 9 月)
这意味着现在可以直接在生产环境用,不需要 polyfill。
场景举例
- 直播播放器:
:stalled触发”网络不佳”提示,纯 CSS - 播客界面:
:muted隐藏音量滑块,用:paused控制封面旋转动画 - 视频编辑器:
:seeking高亮时间轴当前帧 - 可访问性:
video:playing时自动显示字幕容器(配合video:has(:playing))
下一步
这七个伪类看起来是小事,但它代表了一个更大的趋势:浏览器正在把 JavaScript 里的「状态查询」能力变成 CSS 的「状态选择」能力。以前你要写事件、存状态、手动同步 UI;现在只要一行选择器,浏览器替你维护状态。
下一步:打开你的播放器项目,把那堆 addEventListener 翻出来,看看有多少行可以被这七个伪类直接替换。
评论区
0 条评论
登录后可评论。