:focus 和 :active 是交互链基础信号通路,缺失会导致键盘用户无法导航、触摸反馈失效、ios按钮失灵;根本原因在于浏览器判定元素不可交互,需通过 cursor: pointer、touch-action: manipulation 及空 touchstart 监听器激活。

:focus 和 :active 不是“锦上添花”,而是交互链里最基础的信号通路。没配好,键盘用户进不来,触摸反馈就失效,iOS 上按钮点不动——这不是样式问题,是交互通道被堵死了。
为什么 :active 在移动端经常不生效
根本不是 CSS 写错了,而是浏览器(尤其是 Safari)判定元素“不可交互”,直接跳过 :active 触发。常见原因包括:
- 元素没绑定任何事件监听器(哪怕空的
touchstart也行) - CSS 里没写
cursor: pointer—— 它对触摸无用,但却是 Safari 启用:active的开关 - 父级设置了
pointer-events: none,子元素的:active全部静默 - 漏加
touch-action: manipulation,导致 iOS 延迟 300ms 才响应,:active瞬间闪一下就没了
实操建议:给所有按钮类元素统一加这段 CSS:
button, [role="button"], .btn {
cursor: pointer;
touch-action: manipulation;
}
再补一个空 touchstart 监听器(仅需一次):
document.querySelectorAll('button, [role="button"]').forEach(el => {
el.addEventListener('touchstart', () => {});
});
:focus 样式必须保留可访问性通道
:focus 不是给鼠标用户看的,是给键盘 Tab 导航和屏幕阅读器用的。删掉默认 outline 可以,但必须用 box-shadow 或 border 显式替代,否则焦点位置完全不可见。
常见错误:
- 写了
outline: none却没补任何替代样式 → 键盘用户“失明” - 只在
:hover里设高亮,:focus没同步 → 鼠标用户有反馈,键盘用户没有 - 用
tabindex="-1"强行聚焦非可聚焦元素,却不配:focus样式 → 视觉与逻辑脱节
推荐写法(兼顾视觉与可访问性):
button:focus {
outline: none;
box-shadow: 0 0 0 2px #007bff, 0 0 0 4px rgba(0, 123, 191, 0.2);
}
按钮状态切换别直接改 style,用 classList.toggle()
想实现“点击后保持高亮”,比如开关按钮、选中态菜单项,:active 本身做不到——它只在按下瞬间生效。得靠 JS 控制 class。
关键点:
- 避免
element.style.backgroundColor = '...'——破坏 CSS 优先级,无法配合transition - 用
classList.toggle('is-active'),让样式交由 CSS 管理 - 确保对应 class 里写了
transition,否则状态切换生硬
示例:
<button class="toggle-btn">启用</button>
<style>
.toggle-btn { transition: background-color 0.2s, transform 0.15s; }
.toggle-btn.is-active { background-color: #007bff; transform: scale(0.98); }
</style><script>
document.querySelector('.toggle-btn').addEventListener('click', e => {
e.target.classList.toggle('is-active');
});
</script>
原生表单控件优先,别用 div 伪装按钮
<button></button> 和 <input type="checkbox"> 天然支持 :focus、:active、键盘 Enter/Space 触发、VoiceOver 朗读。换成 <div class="btn">,等于主动切断所有标准交互通道。
<p>特别注意:</p>
<ul>
<li>
<code>role="button" 是伪解决方案:不自动获得焦点、不触发 :active、部分 WebView 完全忽略
<button><svg></svg></button>)必须设 min-width: 48px; min-height: 48px,否则触控区域不达标,iOS VoiceOver 会跳过8px,否则滑动切换焦点时容易误跳真正省事的做法,永远是:用对语义化标签,再配好 :focus 和 :active,而不是后期用 JS 补救。











