启用: 只关注键盘使用(或制表符)

我想禁用 :focus时,它不需要,因为我不喜欢我的导航看起来如何,当焦点是它。它使用与 .active相同的样式,而且令人困惑。然而,我不想为使用键盘的人去除它。

我想在主体上添加一个类 enabled-focus,然后使用 body.enabled-focus a:focus{...},但是这会为每个有焦点的元素添加大量额外的 CSS。然后在第一次鼠标移动时从主体中移除该类。

我该怎么做? 还有更好的解决办法吗?

135062 次浏览

这个问题你可能会遇到很多。这些问题的好处是,如果你一旦找到了解决办法,它就不会再困扰你了。

最优雅的解决方案似乎是最简单的: 不要删除: focus 上的轮廓,而是删除: active 上的轮廓——毕竟,: active 是一个动态伪类,它显式地处理可定焦元素被单击或以其他方式激活时应该应用的样式。

a:hover, a:active { outline: none; }

这种方法唯一的小问题是: 如果用户激活一个链接,然后使用浏览器的后退按钮,大纲就会变得可见。哦,而且众所周知,旧版本的 Internet Explorer 会被 focus、 hover 和 active 的确切含义弄混,所以这种方法在 IE6及以下版本中会失败。

小费

有一个简单的解决方案,可以通过添加一个简单的 overflow:hidden来防止轮廓“溢出”,overflow:hidden可以在元素本身的可点击部分检查轮廓。

更新: 这个问题可能不再相关

一些 其他 海报已经提到了 :focus-visible伪类-它现在有不错的 browser support..。

I would like to add, that based on the Spec,其中包含: focus-visible 伪类, browsers should now only 表示注意力集中 when it is helpful to the user - such as in cases where the user interacts with the page via a keyboard or some other non-pointing device

这基本上意味着原来的问题不再相关,因为现在,当用户点击/点击一个按钮(或其他可聚焦元素) ,用户代理将不再显示焦点环-即使按钮被聚焦-因为在这种情况下,焦点环对用户没有帮助。

来自 说明书:

While the :focus pseudo-class always matches the currently-focused 元素,UA 有时仅可见 表示注意力集中(例如通过 绘制一个“焦点环”) ,而不是使用各种启发式来 只有在对 可见焦点的伪类匹配 只有在这种情况下。

事实上,在版本90中,Chromium 的用户代理样式表从 :focus切换到 :focus-visible,由于这个变化,按钮的点击和点击不再调用焦点环

此外,从 版本87开始,Firefox 也使用了: focus-visible 作为用户代理样式。

总之,如果需要自定义焦点样式,因为焦点样式现在已经从 :focus转移到 :focus-visible,当用自定义焦点样式覆盖默认样式时——应该使用 :focus-visible伪类。

就像这样:

button:focus-visible {
/* remove default focus style */
outline: none;
/* custom focus styles */
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}

向后兼容性:

这样使用焦点可见性可能存在的问题是,不支持 :focus-visible的浏览器将显示默认的焦点环,这可能是不清晰或可见的——取决于设计。

Ime Vidas 在 这篇文章中描述了一种可行的策略,目前可以使用: focus-visible 伪类,即使在还不支持的浏览器中也可以工作

开始使用的一个好方法是: 目前可见的焦点是定义焦点 中的样式,然后立即撤消这些相同的样式 焦点: 非(: 焦点-可见)规则 优雅而直观的模式,但它在所有浏览器中都能很好地工作:

Browsers that don’t support :focus-visible use the focus styles defined in the :focus rule and ignore the second style rule completely (因为: focus ——可见对他们来说是未知的)。

在支持焦点-可见的浏览器中,第二个样式规则 方法时,恢复在: focus 规则中定义的焦点样式 : 焦点-可见状态也不活跃。换句话说,焦点 焦点规则中定义的样式只有在下列情况下才有效 * 焦点-可见也是活跃的。

button:focus {
outline: none;
background: #ffdd00; /* gold */
}


button:focus:not(:focus-visible) {
background: white; /* undo gold */
}

原答案:

This excellent article by Roman Komarov poses a viable solution for achieving 只使用键盘的焦点样式 for 纽扣, 链接 and other container elements such as 跨度 or Div (which are artificially made focusable with the tabindex attribute)

解决方案:

button {
-moz-appearance: none;
-webkit-appearance: none;
background: none;
border: none;
outline: none;
font-size: inherit;
}


.btn {
all: initial;
margin: 1em;
display: inline-block;
}


.btn__content {
background: orange;
padding: 1em;
cursor: pointer;
display: inline-block;
}




/* Fixing the Safari bug for `<button>`s overflow */
.btn__content {
position: relative;
}


/* All the states on the inner element */
.btn:hover > .btn__content  {
background: salmon;
}


.btn:active > .btn__content  {
background: darkorange;
}


.btn:focus > .btn__content  {
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}


/* Removing default outline only after we've added our custom one */
.btn:focus,
.btn__content:focus {
outline: none;
}
<h2>Keyboard-only focus styles</h2>


<button id="btn" class="btn" type="button">
<span class="btn__content" tabindex="-1">
I'm a button!
</span>
</button>


<a class="btn" href="#x">
<span class="btn__content" tabindex="-1">
I'm a link!
</span>
</a>


<span class="btn" tabindex="0">
<span class="btn__content" tabindex="-1">
I'm a span!
</span>
</span>


<p>Try clicking any of the the 3 focusable elements above - no focus styles will show</p>
<p>Now try tabbing - behold - focus styles</p>

Codepen

  1. tabindex="-1"将原始交互元素的内容包装在一个附加的内部元素中(参见下面的解释)

所以不要说:

<button id="btn" class="btn" type="button">I'm a button!</button>

do this:

<button id="btn" class="btn" type="button">
<span class="btn__content" tabindex="-1">
I'm a button!
</span>
</button>
  1. 将 css 样式移动到内部元素(布局 css 应该保留在原来的外部元素上)-这样外部元素的宽度/高度就来自内部元素,等等。

  2. 从外部和内部元素中删除默认的焦点样式:

    . btn: 聚焦, . btn _ _ content: focus { 提纲: 无; }

  3. 将焦点样式添加回内部元素 只有当,外部元素有焦点:

    . btn: focus > . btn _ _ content { Box-shadow: 002 px 2 px # 51a7e8;/* 键盘专用焦点样式 < em >/ color: lime; / keyboard-only focus styles */ }

为什么会这样?

这里的诀窍是用 tabindex="-1"设置内部元素——参见 MDN:

负值(通常 tabindex = “-1”表示元素应该 可定焦,但不应通过顺序键盘 导航。

So the element is 集中注意力 via mouse clicks or programatically, but on the other hand - it can't be reached via keyboard 'tabs'.

因此,当单击交互元素时,内部元素获得焦点。没有焦点样式将显示,因为我们已经删除了它们。

.btn:focus,
.btn__content:focus {
outline: none;
}

Note that 在给定的时间只能聚焦1个 DOM 元素 (and document.activeElement returns this element) - so 只有 the inner element will be focused.

On the other hand: when we tab using the keyboard - only the outer element will get the focus (remember: the inner element has tabindex="-1" and isn't reachable via sequential keyboard navigation) [Note that for inherently non-focusable outer elements like a clickable <div> - we have to artificially make them focusable by adding tabindex="0"]

现在,我们的 CSS 启动并将键盘专用的焦点样式添加到 the inner element

.btn:focus > .btn__content  {
box-shadow: 0 0 2px 2px #51a7e8; /* keyboard-only focus styles */
color: lime; /* keyboard-only focus styles */
}

当然,我们要确保当我们选项卡和按 enter时-我们没有破坏我们的交互元素,并且 javascript 会运行。

下面的演示展示了这种情况的确存在,不过请注意,对于诸如按钮和链接等固有的交互元素,你只能免费获得这种功能(例如,按回车键会导致点击事件)。对于其他元素,例如 span,你需要手动编写代码:)

//var elem = Array.prototype.slice.call(document.querySelectorAll('.btn'));
var btns = document.querySelectorAll('.btn');
var fakeBtns = document.querySelectorAll('.btn[tabindex="0"]');




var animate = function() {
console.log('clicked!');
}


var kbAnimate = function(e) {
console.log('clicking fake btn with keyboard tab + enter...');
var code = e.which;
// 13 = Return, 32 = Space
if (code === 13) {
this.click();
}
}


Array.from(btns).forEach(function(element) {
element.addEventListener('click', animate);
});


Array.from(fakeBtns).forEach(function(element) {
element.addEventListener('keydown', kbAnimate);
});
button {
-moz-appearance: none;
-webkit-appearance: none;
background: none;
border: none;
outline: none;
font-size: inherit;
}


.btn {
all: initial;
margin: 1em;
display: inline-block;
}


.btn__content {
background: orange;
padding: 1em;
cursor: pointer;
display: inline-block;
}




/* Fixing the Safari bug for `<button>`s overflow */
.btn__content {
position: relative;
}


/* All the states on the inner element */
.btn:hover > .btn__content  {
background: salmon;
}


.btn:active > .btn__content  {
background: darkorange;
}


.btn:focus > .btn__content  {
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}


/* Removing default outline only after we've added our custom one */
.btn:focus,
.btn__content:focus {
outline: none;
}
<h2>Keyboard-only focus styles</h2>


<button id="btn" class="btn" type="button">
<span class="btn__content" tabindex="-1">
I'm a button!
</span>
</button>


<a class="btn" href="#x">
<span class="btn__content" tabindex="-1">
I'm a link!
</span>
</a>


<span class="btn" tabindex="0">
<span class="btn__content" tabindex="-1">
I'm a span!
</span>
</span>


<p>Try clicking any of the the 3 focusable elements above - no focus styles will show</p>
<p>Now try tabbing + enter - behold - our interactive elements work</p>

Codepen


注意:

  1. 尽管这看起来像是一个过于复杂的解决方案,但对于一个非 javascript 解决方案来说,它实际上是相当令人印象深刻的。涉及到 :hover:active伪类样式的简单的 css“解决方案”根本不起作用。(当然,除非你假设交互元素在点击后立即消失,就像模态中的按钮一样)

button {
-moz-appearance: none;
-webkit-appearance: none;
background: none;
border: none;
font-size: inherit;
}


.btn {
margin: 1em;
display: inline-block;
background: orange;
padding: 1em;
cursor: pointer;
}


.btn:hover, .btn:active {
outline: none;
}
<h2>Remove css :focus outline only on :hover and :active states</h2>


<button class="btn" type="button">I'm a button!</button>


<a class="btn" href="#x">I'm a link!</a>


<span class="btn" tabindex="0">I'm a span!</span>


<h3>Problem: Click on an interactive element.As soon as you hover out - you get the focus styling back - because it is still focused (at least regarding the button and focusable span) </h3>

Codepen

  1. 这个解决方案并不完美: Firefox on windows 仍然会在点击按钮时获得焦点样式——但这似乎是一个 Firefox bug (参见 那篇文章)

  2. 当浏览器实现 :fo­cus-ring伪类时-这个问题可能有一个更简单的解决方案-(参见 那篇文章) 值得一提的是,:focus-ring中有 填充物——参见 Chris DeMars 的这篇文章


一个实用的替代键盘专用焦点样式

因此,实现只使用键盘的焦点样式是非常困难的。much simpler是一个可供选择的解决方案,它既可以满足设计师的期望,也可以被访问——就像你在设计悬停时所做的那样,专注于设计风格。

Codepen

So although technically this is not implementing keyboard-only styles, it essentially removes the need for keyboard-only styles.

在使用 Danield 公认的解决方案时,我发现了一种基于内部/外部 div 概念的替代、更简单的方法。

1)创建一个外部和内部元素,给外部元素 tabindex = “0”和内部元素 tabindex = “-1”

<div role="button" class="outer" tabindex="0">
<span class="inner" tabindex="-1">
I'm a button!
</span>
</div>

2)在 css 中,当聚焦时从内部元素中移除轮廓:

.inner:focus{
outline: none;
}

3)对内部元素应用任何鼠标或单击事件处理程序。将任何焦点事件(onfocus、 onFuzzy、 onkeydown)应用到外部元素。

例如:

<div role="button" class="outer" tabindex="0" onfocus="focusEventHandler()" onkeydown="handleKeyDown.bind(this, myEventHandler)">
<div class="inner" tabindex="-1" onClick="myEventHandler()">
I'm a button!
</div>
</div>

* * 保持尺寸和位置,使内部元素完全与外部元素重叠。定位整个“按钮”与外部元素的样式。

工作原理:

当用户点击“按钮”,他们点击的内部元素,有焦点轮廓删除。不可能单击外部元素,因为它被内部元素覆盖。当用户使用键盘选项卡到“按钮”,他们到达外部元素(tabindex = “0”使得元素可以通过“ tab”到达) ,获得焦点轮廓,但是内部元素不能通过选项卡(tabindex = “-1”)到达,并且当点击时不能获得焦点轮廓。

删除 outline对于可访问性来说是非常糟糕的! 理想情况下,只有当用户 打算使用键盘时焦点环才会显示出来。

2018年答案: 使用 * 焦点-可见。它目前是一个 W3C 的建议,使用 CSS 设计键盘专注的样式。在主流浏览器支持它之前,您可以使用这个健壮的 填料。它不需要添加额外的元素或改变 tabindex

/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
outline: none;
}


/* Optional: Customize .focus-visible */
.focus-visible {
outline-color: lightgreen;
}

我还写了一个更详细的 邮寄,以防你需要更多的信息。

没有明确的解决方案。 我已经做了一个 Hackish 解决方案: 应用主容器上的单击事件,并在单击时写下代码

    _handleMouseClick = (event) => {
if(event.detail){
document.activeElement.blur();
}
}

当您使用鼠标单击时,您将在该单击上获得 event.Details = 1,这将模糊该元素,以便删除轮廓 然后在键盘上点击我们得到 event.Details = 0,所以在键盘情况下表现正常

或者

在 CSS 文件里

     body.disableOutline *:focus{
outline: none !important;
}

In Main js

     document.addEventListener('click', _handleMouseClick,true);
document.addEventListener('keydown',_keydown,true);
function _handleMouseClick(event){
if(event.detail){
document.getElementsByTagName("body")[0].classList.add("disableOutline");
}
}
function _keydown(e){
document.getElementsByTagName("body")[0].classList.remove("disableOutline");
}

案例研究: Facebook 登录页面

Facebook 现在(2018年6月)正在他们的登录页面上使用一点 Javascript。

Javascript 检测用户何时单击鼠标或使用键盘,并在主体上切换一个类: <body class="using-mouse">

然后 CSS 规则可以使用该类显示或隐藏相关元素上的适当焦点样式。

下面是一些示例代码(也可以使用 在 CodePen 上)。

// Let the document know when the mouse is being used
document.body.addEventListener('mousedown', function() {
document.body.classList.add('using-mouse');
});


// Re-enable focus styling when Tab is pressed
document.body.addEventListener('keydown', function(event) {
if (event.keyCode === 9) {
document.body.classList.remove('using-mouse');
}
});


// Alternatively, re-enable focus styling when any key is pressed
//document.body.addEventListener('keydown', function() {
//  document.body.classList.remove('using-mouse');
//});
/* The default outline styling, for greatest accessibility. */
/* You can skip this to just use the browser's defaults. */
:focus {
outline: #08f auto 2px;
}


/* When mouse is detected, ALL focused elements have outline removed. */
body.using-mouse :focus {
outline: none;
}
<input>
<button>Submit</button>

注意,上面的 :focus等效于 *:focus,匹配所有元素。如果只想从按钮中移除样式,可以将 button:focus放在那里。


案例研究: GMail 登录页面

另外,当时 GMail 只是在设计聚焦按钮的样式时使用了比未聚焦按钮更重的阴影,而不管用户使用的是鼠标还是键盘。

这很容易实现和理解,并且不需要任何 Javascript。

:focus {
outline: none;
box-shadow: 0 0px 16px #0005;
}

但这是一种妥协。它传达了鼠标用户并不真正感兴趣的焦点信息,对于键盘用户来说,它可能有点像 太狡猾了

尽管如此,这种折衷方案可能比两种极端方案中的任何一种都要好(对所有用户来说都是一个强有力的大纲,或者根本就没有大纲)。


StackOverflow 的主按钮使用了与 GMail 类似的方法,但外观更加风格化:

box-shadow: inset 0 1px 0 0 rgba(102,191,255,0.5), 0 0 0 4px rgba(0,149,255,0.15);

就个人而言,我会使用一个更强(高对比度)的颜色,为可访问性。

&:focus:not(:hover) { }

它不会在100% 的情况下工作,但我认为对于大多数人的需要,这应该是足够的。

它将防止点击时触发 :focus状态,因为鼠标必须悬停在元素上方才能点击它。

Https://codepen.io/heyvian/pen/eopoxr

UPDATE 2020

:focus-visible已经登陆稳定的 Chrome。赶紧用吧!只有 IE 和 Safari 支持丢失,所以添加一个备份(如下)。

需要一个很好的特性查询来区分 IE11 + Safari 和其他浏览器吗:

@mixin focus-visible-fallback {
@supports (contain: none) {
&:focus-visible {
outline: none;
@content;
}
}


/* Safari & IE11 */
@supports not (contain: none) {
&:focus {
outline: none;
@content;
}
}
}

注意 : 正如评论中提到的,无论使用 :focus还是 :focus-visible<input>总是能够获得焦点。

原帖

在所有流行的常绿浏览器中都没有 :focus-visible之前,您可以在 CSS 的全局部分使用这个简单的技巧,而不需要任何填充:

@media (pointer: coarse) {
*:focus {
outline: none;
}
}

然后像往常一样使用 :focus添加焦点效果。

此时,您可能已经了解到,从可访问性的角度来看,将 outline: none默认设置为有焦点的元素是一个糟糕的想法。确实如此。

但是,如果您在 pointer: coarse媒体查询中使用此规则,那么它将变得非常有用,因为它只适用于移动电话和平板电脑,而不适用于台式机。这正是你想要的。

我唯一能想到的问题是带键盘的移动用户,他们使用键盘点击内容,但我不确定是否有很多这样的用户。因此,最终 :focus-visible将是一个更好的解决方案,但就目前而言,这应该足够了。

Blueprintjs 库为此提供了一个很好的解决方案。

Https://blueprintjs.com/docs/#core/accessibility

然而,我不能理解他们是如何做到的。

正如其他人提到的那样,有一个 :focus-visible选项,它有很好的浏览器支持 https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible

I found very useful this article and wanted to share it https://css-tricks.com/keyboard-only-focus-styles/

例子:

button {


&:focus-visible {
border: 2px solid #004EA3;
}
}

As several has stated, :focus-visible is the way to go in terms of a pure CSS solution. I want to provide the easiest way I've solved this styling issue using CSS only, however it has some drawbacks in browser support and is not useful for everyone:

@supports not selector(:focus-visible) {
:focus {
// Fallback styles for browsers that doesn't support :focus-visible.
}
}


:focus-visible {
// Tab focus styles
}

只应用 :focus的情况下,它真的需要防止与 :focus-visible样式的干扰。不支持 :focus-visible的浏览器会简单地忽略它,因此不需要 @supports之类的东西。

了解更多关于浏览器对 @ 支持选择器* 焦点-可见的支持。

如果您碰巧像我一样在 JS 中使用 CSS,那么您可以利用 CSS.support ()有条件地呈现备用 CSS,以防您需要关心不支持 @supports selector的浏览器。类似于 if (CSS.supports("selector(:focus-visible)")) {...}的东西。