2021年实现仅键盘生效的:focus焦点样式的最优方案探讨
长期以来,前端开发者为了贴合品牌视觉设计要求,通常会通过CSS的outline: none;移除按钮等可交互元素的默认焦点轮廓。这种操作虽然能让页面视觉更统一,但会直接降低站点可访问性,大幅提升依赖键盘导航的残障用户的站点操作难度。
这一问题的核心痛点长期没有低成本的解决方案:我们始终没有简单的方式区分焦点事件的触发源——不管是鼠标点击触发的聚焦,还是按下Tab键触发的聚焦,都会触发完全相同的focus状态。
这是前端领域已经持续多年的热门讨论方向,目前主流的实现「仅键盘操作时显示焦点样式」的方案共有4种,各有优劣:
悬停/焦点样式对齐方案
部分站点(如Target.com)会为鼠标悬停、焦点状态设置完全相同的视觉样式,因此不需要移除焦点轮廓。
优势:绕开了焦点触发源区分的核心问题
劣势:设计自由度受限,无法做差异化的悬停/焦点视觉设计
示例代码:button { width: 180px; height: 60px; background: #999; border: none; } button:focus, button:hover { outline: 5px solid green; }<button>Click or Tab me!</button>JS焦点源检测方案
部分站点(如Apple.com)会使用成熟的可访问性工具,为可交互元素添加焦点触发源标记属性;这类工具中广获认可的包括焦点源检测库、what-input输入源检测库,以及配套的浏览器兼容polyfill。
优势:可兼容所有浏览器,不受设计要求限制,实现灵活度高
劣势:需要额外加载JS资源,相比纯CSS方案有更高的性能开销
示例代码:// 获取当前焦点触发源 ally.style.focusSource().current()button { width: 180px; height: 60px; background: #999; border: none; outline: none; } html[data-focus-source="key"] button:focus { outline: 5px solid green; }<button>Click or Tab me!</button> <!-- 引入焦点源检测库 --> <script src="ally.min.js"></script>原生
:focus-visible伪类方案:focus-within是业界公认的优雅聚焦场景适配方案,而:focus-visible本质就是专门针对键盘触发场景设计的伪类,仅在元素通过键盘操作聚焦时匹配规则(该伪类曾用名:focus-ring),目前已在绝大多数现代浏览器中得到支持。
优势:纯CSS实现,性能消耗极低,开发实现成本低
劣势:早期浏览器支持度不完善,Safari长期未做适配;目前Safari已在技术预览版本中默认启用该伪类,正式版本的全量覆盖正在推进
示例代码:button { width: 180px; height: 60px; background: #999; border: none; outline: none; } button:focus-visible { outline: 5px solid green; }<button>Click or Tab me!</button>tabindex="-1"嵌套hack方案
开发者Roman Komarov曾提出无需JS的兼容hack:在按钮元素内部嵌套一个HTML语法完全合法的<span>标签,为该span设置tabindex="-1"属性并单独配置焦点样式,即可区分不同的焦点触发源。
优势:无需引入额外JS资源,可兼容所有浏览器
劣势:需要为站点内所有按钮、链接内部统一嵌套span标签,还要将原有按钮/链接的内边距迁移到内部span,同时需要同步调整GTM等各类埋点的触发逻辑,改造成本极高
示例代码:button { width: 180px; height: 60px; background: #999; border: none; outline: none; } button > span { display: flex; justify-content: center; height: 100%; align-items: center; } button:focus { outline: 0; } button:focus > span { outline: 5px solid green; } [tabindex="-1"]:focus { outline: none !important; }<button> <span tabindex="-1">Click or Tab me!</span> </button>
从当前技术演进趋势看,:focus-visible已经成为W3C定义的标准行为,浏览器厂商的支持度正在快速完善,是目前行业普遍推荐的最优实现方案。如果需要兼容极旧版本的浏览器,仅需搭配轻量的官方polyfill做兜底,不需要额外维护复杂的自定义逻辑。
内容的提问来源于stack exchange,提问作者cMarius

