css选择器中的:focus与:active交互效果实现

:focus 在元素获得键盘焦点时生效,:active 仅在鼠标/触控按下到松开的瞬间触发;二者触发时机不同、可共存,且需结合 JS 和 :focus-visible 等方案应对多端兼容性问题。

:focus 和 :active 的触发时机完全不同

很多人以为 :focus:active 是“按下去就同时生效”的,其实不是::focus 表示元素获得键盘焦点(比如 Tab 切入、input 被点击或调用 .focus()),而 :active 仅在鼠标/触控按下(mousedown / touchstart)到松开(mouseup / touchend)的瞬间生效,持续时间极短,且不依赖焦点状态。

这意味着:

  • button:active 在点击时亮一下,哪怕它没 tabindex 或没被聚焦过
  • button:focus 在键盘导航抵达时保持高亮,但点击时不一定触发 :active(比如鼠标点太快、系统延迟、或被 preventDefault 干扰)
  • 两者可以共存:例如用键盘 Tab 到按钮后,再用鼠标点击——此时同时匹配 :focus:active

如何让按钮点击时既有 :active 反馈又保持 :focus 高亮

默认情况下,点击按钮会触发 :active,但松开后焦点可能丢失(尤其在 `,然后在 JS 中切换临时 class(如 is-pressing)来驱动样式

  • 注意:添加 ontouchstart 后,:active 会在 touchstart 触发时立即生效,而非等到 touchend,行为与桌面端略有差异
  • :focus-within 作为 :focus/:active 的补充方案

    当目标不是单个按钮,而是整个卡片、表单项区域需要“任一子元素聚焦或激活时整体响应”,:focus-within 就比堆砌多个 :focus:active 更简洁可靠。

    典型场景:

    • 一个 div.card 包含 inputbutton,希望输入框获得焦点或按钮被点击时,整个卡片加边框
    • 写法:
      .card:focus-within {
        border: 2px solid #007bff;
      }
      .card:focus-within button:active {
        transform: translateY(1px);
      }
    • :focus-within 会响应子元素的 :focus,也响应子元素的 :active(只要该子元素本身支持 :active
    • 兼容性:Chrome 60+、Firefox 61+、Safari 15.4+,IE 完全不支持;如需兼容老版本,仍需 JS 监听 + class 切换
    真正难处理的不是语法,而是不同输入方式(键盘、鼠标、触摸、辅助技术)下焦点流和激活态的交错逻辑。别指望纯 CSS 覆盖所有路径,关键交互点该加 JS 控制时就加,别硬套伪类。