启用:仅专注于键盘使用(或按Tab键)


78

我想在:focus不需要时禁用它,因为我不喜欢焦点放在导航上时的外观。它使用与相同的样式,.active并且令人困惑。但是我不想为使用键盘的人摆脱它。

我当时想enabled-focus在按Tab时在正文上添加一个类,然后添加一个类,body.enabled-focus a:focus{...}但这将为每个具有焦点的元素添加很多额外的CSS。然后,首先将鼠标从身体上移开该类。

我将如何处理?有更好的解决方案吗?


在jquery中添加一个事件侦听器,以获取所需的特定键,如果按下它们,则仅用于addClass()要具有该焦点样式的元素。
odedta

仅提供CSS解决方案,但仅适用于Firefox。这是W3C提案css-tricks.com/keyboard-only-focus-styles
jcubic

Answers:


100

这种优良制品罗马科马罗夫构成用于实现可行的解决方案仅键盘聚焦样式按钮链接及其它容器元件,例如跨度div的(其是人工制造可对焦用的tabindex属性)

解决方案:

码笔

1)将原始交互元素的内容包装在另一个内部元素中tabindex="-1"(请参见下面的说明)

因此,与其说:

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

做这个:

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

2)将css样式移至内部元素(布局css应保留在原始外部元素上)-这样外部元素的宽度/高度将来自内部元素等。

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

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

4)仅当外部元素具有焦点将焦点样式添加回内部元素:

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

为什么这样做?

这里的技巧是使用tabindex="-1"-设置MDN设置内部元素:

负值(通常为tabindex =“-1”表示该元素应该可聚焦,但不应通过顺序键盘导航来访问...

因此,该元素通过单击鼠标或以编程方式进行聚焦,但另一方面-无法通过键盘的“选项卡”访问。

因此,当单击交互式元素时,内部元素将获得焦点。由于我们已删除焦点样式,因此不会显示。

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

请注意,在给定的时间只能集中1个DOM元素(并document.activeElement返回此元素)-因此,内部元素将被聚焦。

另一方面:当我们使用键盘进行制表时-只有外部元素会获得焦点(请记住:内部元素具有tabindex =“-1”并且无法通过顺序键盘导航来访问)[请注意,对于固有的非可聚焦的外部元素(如可点击的元素)<div>-我们必须通过添加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将运行。

这是一个演示,表明确实是这种情况,请注意,尽管您仅免费获得了固有交互元素(例如按钮和链接)(例如,跨度-您需要手动进行编码:)

码笔


注意:

1)尽管这似乎是一个过于复杂的解决方案,但是对于非JavaScript解决方案而言,它确实令人印象深刻。简单的纯css的“解决方案”:hover:active伪类样式都根本不起作用。(当然,除非您假设交互式元素在单击时像模态提示中的按钮一样立即消失)

码笔

2)此解决方案并不完美:Windows上的Firefox仍将获得单击按钮的焦点样式-但这似乎是Firefox的错误(请参阅文章

3)当浏览器实现:focus-ring伪类时-这个问题可能有一个更简单的解决方案-(请参阅文章)对于它的价值,这里有一个polyfill用于:focus-ring-参见Chris DeMars的本文。


实用的替代仅键盘的焦点样式

因此,实现仅键盘的焦点样式非常困难。一种更简单且可能既满足设计师的期望又可以访问的替代方法/解决方法是将样式设置为焦点,就像为悬停样式一样。

码笔

因此,尽管从技术上讲这不是在实现仅键盘样式,但从本质上讲,它消除了对仅键盘样式的需要。


1
Windows上的Firefox错误似乎从FF 60起就不存在了。使用:not(:hover):focus选择器似乎可以解决问题。
挪威

:not(:hover):focus由于上面描述的问题,@ wegry并不是一个很好的解决方案:“一旦悬停,就可以重新获得焦点样式-因为它仍然处于焦点状态(至少在按钮和可聚焦范围方面)”
Zbynek

@Zbynek我同意,但是过去我将其用作权宜之计。
wegry

如果我们输入type =“ radio”怎么实现?
thewebtud

79

案例研究: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);

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


5
我喜欢Facebook解决方案,尽管它使用Javascript。代码足够简单易懂,并且CSS完全掌握在您手中。我想我将开始使用它。谢谢。
Bram Vanroy

3
出色的简单解决方案。
加布里埃尔·罗德里格斯

3
我认为,这种解决方案比公认的解决方案更好,而且没有太多麻烦。而且,它不需要太多的标记混乱。
CunningFatalist

1
最佳解决方案+
马斯

1
@IOIIOOIO是的,他们的行为已更改。如今,它们似乎仅在按Tab时重新启用焦点样式,并且有一个空白/错误字段。但实际上,我认为您提出的建议是最直观的(POLA),因此我更新了答案以使其符合要求。谢谢!
joeytwiddle

40

拆卸outline很容易造成伤害!理想情况下,仅当用户打算使用键盘时才会显示聚焦环。

2018年答案:使用:focus-visible。目前,这是W3C提案,旨在使用CSS设置仅键盘焦点的样式。在主流浏览器支持之前,您可以使用此强大的polyfill。不需要添加额外的元素或更改tabindex

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

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

我还写了一篇更详细的文章,以防万一您需要更多信息。


2
超!拥有+1互联网。
joeytwiddle

1
哎呀,为什么还没有这个标准。亲爱的未来的读者,我羡慕你。
矮胖的Chunk,

19

这是您可能会遇到很多的问题。这些问题的好处是,一旦找到解决方案,它就不会再打扰您了。

最优雅的解决方案似乎是最简单的解决方案:不要删除:focus上的轮廓,而要使用:active代替它–毕竟,:active是动态伪类,用于显式处理当单击或激活了可聚焦元素。

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

此方法的唯一小问题:如果用户激活链接然后使用浏览器的后退按钮,则轮廓将可见。哦,众所周知,旧版本的Internet Explorer被:focus,:hover和:active的确切含义弄糊涂了,因此该方法在IE6及以下版本中失败。

Tipp

有一个简单的解决方法,通过添加一个simple来防止轮廓“溢出” overflow:hidden,从而使轮廓始终围绕元素本身的可单击部分进行检查。


1
那真是有见地!谢谢。
米罗2015年

2
那按钮呢?
DanV

4

在研究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,onblur,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”),并且单击时不会收到焦点轮廓。


您能否继续说明为什么它比公认的解决方案更好/更简单?
塔林


1

2020年更新

:focus-visible已登陆稳定的Chrome。就已经使用了!

原始帖子

:focus-visible在所有流行的常绿浏览器中都没有这种方法之前,您可以在CSS的全局部分使用此简单技巧,而无需任何polyfill:

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

然后使用通常的方法添加焦点效果:focus

在这一点上,您可能已经了解到,outline: none;从可访问性的角度来看,默认情况下将焦点设置为焦点元素是一个可怕的想法。确实是这样。

但是,如果在pointer: coarse媒体查询中将此规则作为范围,它将非常有用,因为它仅适用于手机和平板电脑,不适用于台式机。这正是您想要实现的目标。

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


这似乎在台式机上也不起作用jsfiddle.net/mirohristov/Lovp947w 您可以提供有效的演示吗?
米罗

@Miro并不一定要在桌面上“工作”,请注意这pointer: coarse一点。这是一种仅在移动设备(电话,平板电脑或没有精细定位设备(例如鼠标或触摸板/触控板)的设备)上隐藏焦点轮廓的解决方法。
神经递质

我懂了。我以为你在回答我的问题。(这也与台式机有关)
Miro

是的,但不是真正的……对于那些最终:focus-visible在主要浏览器中实现之前寻求实用解决方案的人们来说,这更像是一种解决方法。这是正确的,因为答案并不能解决问题中列出的所有问题,在这种情况下,您可以正确地投票否决-不会冒犯。
神经递质

2020年即将到来,并且:focus-visible已在Chrome中实现。重要的里程碑!
神经递质

0

没有明确的解决方案。我已经完成了一种Hackish解决方案:在您的主容器上应用click事件,并在click上编写以下代码

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

当您使用鼠标单击时,将获得event.detail = 1,使该元素模糊,以便删除轮廓;在键盘单击时,我们将得到event.detail = 0,因此在键盘情况下表现正常

要么

在css文件中

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

在主要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");
            }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.