2021 年实现仅键盘 :focus 样式的最佳方式是什么?

Cud*_*ead 5 html javascript css accessibility focus

长期以来,通过 CSS 从按钮等可交互元素中删除轮廓一直是常见的做法outline: none;,因为大多数时候它看起来不符合品牌,但它阻碍了可访问性,并使依赖键盘导航的残障人士更难浏览网站。

问题是我还没有找到一种简单的方法来区分鼠标/键盘之间的焦点事件源,并且单击以及键盘选项卡元素将触发焦点状态。

我知道这个问题一直是一个热门话题,但大多数来源都有好几年了,所以我将展示我发现的关于如何实现仅键盘焦点的 4 种主要方法,每种方法都有自己的优缺点:

  1. 作弊”方法,一些网站(例如 Target.com)具有相同的悬停和聚焦样式,这种设计选择使他们可以选择根本不需要删除焦点轮廓。

优点:解决了问题。

缺点:设计选项有限。

button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
}

button:focus,
button:hover { 
  outline: 5px solid green; 
}
Run Code Online (Sandbox Code Playgroud)
<button>Click or Tab me!</button>
Run Code Online (Sandbox Code Playgroud)

  1. JS 解决方案,Apple.com 等网站使用ally.jsdata-focus-method向其可交互元素添加属性。What-input也很出名,并且与其他各种Polyfill一起做得很好。

优点:在所有浏览器上都能正常运行,无论设计如何,都可以实现。

缺点:需要额外的 http 请求来获取 .js 文件,与其他解决方案相比,JS 对性能的负担更大。

ally.style.focusSource().current()
Run Code Online (Sandbox Code Playgroud)
button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
  outline: none;
}

html[data-focus-source="key"] button:focus {
  outline: 5px solid green;
}
Run Code Online (Sandbox Code Playgroud)
<button>Click or Tab me!</button>
<script src="https://cdn.jsdelivr.net/ally.js/1.4.1/ally.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

  1. :focus-within优雅解决方案的圣杯。:focus-visible基本上是纯键盘版本,并且已慢慢在大多数现代浏览器:focus上可用(以前)。:focus-ring

优点: CSS 解决方案,性能简单,易于实施。

缺点:浏览器支持更好,但由于 Safari 尚未支持,仍然不理想。

编辑:自 2022 年 3 月 14 日起已由 Safari默认:focus-visible启用

button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
  outline: none;
}

button:focus-visible {
  outline: 5px solid green;
}
Run Code Online (Sandbox Code Playgroud)
<button>Click or Tab me!</button>
Run Code Online (Sandbox Code Playgroud)

  1. Roman Komarov tabindex="-1" hack 表明我们可以通过在元素span内部添加一个button(技术上有效的 HTML)然后给出跨度tabindex="-1"和不同的焦点样式来区分焦点样式。

优点:不涉及 JS,似乎在所有浏览器上都能正常工作。

缺点:需要在网站上的所有按钮和链接内进行垃圾邮件跨度,需要将填充从按钮/链接移动到内部跨度,需要调整各种跟踪标签,例如 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;
}
Run Code Online (Sandbox Code Playgroud)
<button>
  <span tabindex="-1">Click or Tab me!</span>
</button>
Run Code Online (Sandbox Code Playgroud)

现在的问题是,是否有更好或更简单的方法来实现此功能?我是否缺少这方面的行业标准?

Gra*_*hie 3

现在的问题是,是否有更好或更简单的方法来实现此功能?我是否缺少这方面的行业标准?

简短的回答:不,如果您的目标是“完美”(它在所有浏览器中的工作原理完全相同),那么您基本上已经在此处列出了您的选项。

然而,正如您所说,所有 4 个选项都有缺点。

就我个人而言,我会选择“最适合”的解决方案,其中一些用户可能最终会在点击时获得焦点指示器,但大多数新浏览器都会优雅地处理事情:

button:focus { 
outline: 3px solid #333;
outline-offset: 3px;
 }
button:focus:not(:focus-visible) {
    outline: none;
    outline-offset: 0;
}
Run Code Online (Sandbox Code Playgroud)
<button>A test</button>
<button>Another test</button>
Run Code Online (Sandbox Code Playgroud)

该解决方案的缺点是什么?

Safari 仍会在点击时显示焦点指示器……没关系!

Safari最终会赶上并使用:focus-visible。在那之前,上面的小提琴是确保支持的浏览器:focus-visible按预期运行并且不支持它的浏览器回退到在单击时提供焦点指示器的最简单方法。

但它必须是完美的

然后我会使用基于支持有条件加载的填充。

这个答案详细说明了如何检测对:focus-visible

此时您可以有条件地加载一个:focus-visiblepolyfill(gzip 后为 3.5kb)。

我个人不喜欢这个解决方案,因为你必须更改所有 CSS......但我并不像大多数人一样被点击时显示的焦点指示器所困扰,所以我可能有偏见!