播视频时播的按钮要淡出暂停的要出来——今天 CSS 自己会问了

播视频的时候,页面上那个大大的播放按钮播的时候要淡出、暂停的时候要出来——这件事以前要靠 JS 同步两套状态。今天 CSS 告诉你:你自己问浏览器就行了,不用问 JS。

Chrome 152 稳定版上周刚出,里面藏了七个媒体状态伪类::playing:paused:seeking:buffering:stalled:muted:volume-locked。它们的作用很直接——CSS 直接读 <video><audio> 现在的播放状态,不需要你写一行 JS class。

怎么用?配合 :has() 就行:

/* 播放中:隐藏播放按钮,显示进度条 */
.player:has(video:playing) .play-button {
  opacity: 0.4;
}
.player:has(video:playing) .progress-bar {
  opacity: 1;
}

/* 暂停中:显示播放按钮 */
.player:has(video:paused) .play-button {
  opacity: 1;
}

/* 缓冲中或卡住:显示加载转圈 */
.player:has(video:buffering, video:stalled) .spinner {
  opacity: 1;
}

/* 静音或音量锁定:高亮静音按钮 */
.player:has(video:muted, video:volume-locked) .mute-chip {
  opacity: 1;
  background: var(--active-bg);
}

这就是以前要靠 JS 的 setState 做的事——监听 play / pause 事件,然后手动 add/remove 一个 class 给 CSS 读。现在 CSS 直接问浏览器,零 JS class 同步,零状态滞后。

为什么这个思路根本不一样?

以前播放状态的 owner 是 JS。浏览器知道播没播,但 JS 要把这个状态「翻译」给 CSS,所以中间有一层手动同步。

问题就出在这层同步上:事件触发晚了一拍,或者你漏了某个触边路径,CSS 读到的状态就错了——按钮显示「播放中」但视频其实已经停了,用户点一下以为点到了暂停。

媒体状态伪类把 owner 换了:浏览器直接暴露状态,CSS 自己读。JS 只需要管播放逻辑本身,不管状态怎么传递给 CSS。

JS 还要做什么?

这七个伪类不是来抢你所有活的。它们接管的是「CSS 状态桥」这一层。

JS 还是要写:播放/暂停的触发逻辑、analytics 埋点、预加载策略、自定义快捷键。它们不管这些。

浏览器支持情况

这个功能比较特殊——Safari 15.4(2022年)就支持了,Firefox 150(2026年4月)跟上,Chrome 152(2026年8月25日)刚刚稳定,所以目前还没到 Baseline。caniuse 数据显示全球覆盖率约 17.62%,主要是 Chromium 系还没完全跟上来。

但这不影响你在 Safari 和 Firefox 上用。可以用 @supports 做渐进增强:

@supports selector(video:playing) {
  .player:has(video:playing) .play-button {
    opacity: 0.4;
  }
}

下一步:你现在的播放器要不要改?

如果你的播放器满足以下任一条件,可以开始评估迁移:

  1. 播放/暂停时 CSS 状态是通过 JS class 同步的
  2. 有多个播放状态相关的 UI 元素(按钮、进度条、静音按钮、缓冲转圈等)
  3. 有过因为状态滞后导致 UI 显示错乱的 bug

迁移成本很低——把 JS 里 element.classList.add('playing') 删掉,加上 :has(video:playing) 选择器就行。不需要动播放逻辑本身。

Chrome 152 刚刚稳定,接下来 Firefox 和 Edge 也会快速跟上来。这个功能是 Interop 2026 的重点方向之一,跨浏览器一致性已经不是问题——问题是你的播放器还没有迁移。

评论区

0 条评论

登录后可评论。