样式聚焦

聚焦指示器(通常用“聚焦环”表示)用于标识网页上当前聚焦的元素。对于无法使用鼠标的用户,此指示器极为重要,因为它充当鼠标指针的替代。

如果浏览器的默认焦点指示器与您的设计冲突,您可以使用 CSS 对其重新设置样式。不过,请务必考虑键盘用户!

无论使用哪种输入设备(鼠标、键盘、触控笔等)或用于聚焦该元素的方法,每次聚焦到某个元素时,都会应用 :focus 伪类。例如,下面的 <div> 具有使其可聚焦的 tabindex。它还为 :focus 状态提供了自定义样式:

div[tabindex="0"]:focus {
  outline: 4px dashed orange;
}

无论您是使用鼠标点击它,还是使用键盘来点击它,<div> 看起来始终相同。

遗憾的是,浏览器在应用焦点的方式上可能不一致。某个元素是否获得焦点可能取决于浏览器和操作系统。

例如,以下 <button> 还为其 :focus 状态提供了自定义样式。

button:focus {
  outline: 4px dashed orange;
}

如果您在 macOS 上的 Chrome 中使用鼠标点击 <button>,您应该会看到其自定义焦点样式。不过,如果您在 macOS 上的 Safari 中点击 <button>,则不会看到自定义焦点样式。这是因为在 Safari 中,当您点击某个元素时,该元素不会获得焦点。

由于焦点的行为不一致,因此可能需要在不同设备上进行一些测试,以确保焦点样式能被用户接受。

使用 :focus-visible 选择性地显示焦点指示器

每当元素获得焦点且浏览器通过启发词语确定显示焦点指示器对用户有益时,系统都会应用新的 :focus-visible 伪类。具体而言,如果最近一次用户互动是通过键盘进行的,并且按键操作不包含元键、ALT / OPTIONCONTROL 键,则 :focus-visible 将匹配。

以下示例中的按钮将选择性显示焦点指示标志。如果您使用鼠标点击它,结果会与先使用键盘按 Tab 键将焦点移至它时不同。

button:focus-visible {
  outline: 4px dashed orange;
}

使用 :focus-within 为聚焦元素的父元素设置样式

当元素本身获得焦点或该元素内的其他元素获得焦点时,系统会将 :focus-within 伪类应用于该元素。

它可用于突出显示网页的某个区域,以吸引用户注意该区域。例如,当用户选择该表单本身时,以及选择其任何单选按钮时,下方表单都会获得焦点。

form:focus-within {
  background: #ffecb3;
}

何时显示焦点指示器

一个很好的经验法则是,问问自己:“如果您在使用移动设备时点击此控件,是否希望系统显示键盘?”

如果答案为“是”,则无论使用哪种输入设备为控件聚焦,该控件都应始终显示焦点指示器。<input type="text"> 元素就是一个很好的例子。无论输入元素最初是如何获得焦点的,用户都需要通过键盘向该元素发送输入,因此始终显示焦点指示器会很有帮助。

如果答案为“否”,该控件便可选择性地显示焦点指示标志。<button> 元素就是一个很好的例子。如果用户使用鼠标或触摸屏点击该按钮,操作便会完成,因此可能不需要焦点指示器。不过,如果用户使用键盘导航,则显示焦点指示器会很有用,以便用户决定是否要使用 ENTERSPACE 键点击控件。

避免使用 outline: none

坦率地说,浏览器决定何时绘制焦点指示器的方式非常混乱。使用 CSS 更改 <button> 元素的外观或为元素设置 tabindex 会导致浏览器的默认焦点圈行为生效。

一种很常见的反模式是使用 CSS 移除焦点指示器,例如:

/* Don't do this!!! */
:focus {
  outline: none;
}

如需解决此问题,更好的方法是组合使用 :focus:focus-visible polyfill。以下代码块演示了 polyfill 的运作方式,下方的示例应用提供了使用 polyfill 更改按钮上的焦点指示器的示例。

/*
  This will hide the focus indicator if the element receives focus via the
  mouse, but it will still show up on keyboard focus.
*/
.js-focus-visible :focus:not(.focus-visible) {
  outline: none;
}

/*
  Optionally: Define a strong focus indicator for keyboard focus.
  If you choose to skip this step, then the browser's default focus
  indicator will be displayed instead.
*/
.js-focus-visible .focus-visible {
  
}