启用 :focus only on keyboard use (or tab press)
Enable :focus only on keyboard use (or tab press)
我想在不需要时禁用 :focus
,因为我不喜欢焦点位于其上时导航的外观。它使用与 .active
相同的样式并且令人困惑。但是我不想为使用键盘的人摆脱它。
我想在按下 Tab 键时在正文中添加一个 class enabled-focus
然后添加 body.enabled-focus a:focus{...}
但这会为每个添加很多额外的 CSS具有焦点的元素。然后在第一次按下鼠标时从正文中删除 class。
我该怎么做?有更好的解决方案吗?
这个问题你可能会经常遇到。这类问题的好处是,一旦你找到了解决办法,就不会再困扰你了。
最优雅的解决方案似乎是最简单的:不要删除 :focus 上的大纲,而是在 :active 上删除 - 毕竟,:active 是动态伪 class 显式处理单击或以其他方式激活可聚焦元素时应应用的样式。
a:hover, a:active { outline: none; }
此方法唯一的小问题:如果用户激活 link 然后使用浏览器的后退按钮,轮廓就会变得可见。哦,众所周知,旧版本的 Internet Explorer 会混淆 :focus、:hover 和 :active 的确切含义,因此此方法在 IE6 及以下版本中失败。
提示
有一个简单的解决方法可以通过添加一个简单的 overflow:hidden
来防止轮廓“溢出”,它可以检查元素本身可点击部分周围的轮廓。
更新:这个问题可能不再相关
have mentioned the :focus-visible
pseudo class - which now has decent browser support...
我想补充一点,基于 spec which covers the :focus-visible pseudo class,浏览器现在应该仅在对用户有帮助时 指示焦点 - 例如在以下情况下用户通过键盘或其他一些非指点设备与页面交互
这基本上意味着原始问题不再相关,因为现在,当用户 clicks/taps 一个按钮(或另一个可聚焦元素)时,用户代理将不再显示聚焦环 - 甚至尽管按钮已聚焦 - 因为在这种情况下聚焦环对用户没有帮助。
来自 the spec:
While the :focus pseudo-class always matches the currently-focused
element, UAs only sometimes visibly indicate focus (such as by
drawing a “focus ring”), instead using a variety of heuristics to
visibly indicate the focus only when it would be most helpful to the
user. The :focus-visible pseudo-class matches a focused element in
these situations only...
事实上,从版本 90 开始,Chromium 的用户代理样式表从 :focus
切换到 :focus-visible,并且由于这一变化,按钮点击不再调用焦点环
此外,从 version 87 开始,Firefox 还在其用户代理样式中使用 :focus-visible。
综上所述,如果需要自定义焦点样式,因为焦点样式现在已从 :focus
转移到 :focus-visible
,当使用自定义焦点样式覆盖默认样式时 - :focus-visible
应该使用伪 class。
像这样:
button:focus-visible {
/* remove default focus style */
outline: none;
/* custom focus styles */
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}
向后兼容性:
像这样使用 :focus-visible 的可能问题是,不支持 :focus-visible
的浏览器将显示默认的焦点环,它可能不清晰或不可见 - 取决于设计.
Šime Vidas,在 this article 中描述了当前使用 :focus-visible 伪 class 的可行策略 - 即使在尚不支持 :focus-visible 的浏览器中也可以使用-
A good way to start using :focus-visible today is to define the focus
styles in a :focus rule and then immediately undo these same styles in
a :focus:not(:focus-visible) rule. This is admittedly not the most
elegant and intuitive pattern, but it works well in all browsers:
Browsers that don’t support :focus-visible use the focus styles
defined in the :focus rule and ignore the second style rule completely
(because :focus-visible is unknown to them).
In browsers that do support :focus-visible, the second style rule
reverts the focus styles defined in the :focus rule if the
:focus-visible state isn’t active as well. In other words, the focus
styles defined in the :focus rule are only in effect when
:focus-visible is also active.
button:focus {
outline: none;
background: #ffdd00; /* gold */
}
button:focus:not(:focus-visible) {
background: white; /* undo gold */
}
原答案:
This excellent article by Roman Komarov 提出了实现 仅键盘焦点样式 用于 按钮 、 链接的可行解决方案 和其他容器元素,例如 spans 或 divs(通过 tabindex 属性人为地使其可聚焦)
解决方案:
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
- 用
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>
将 css 样式移动到内部元素(布局 css 应保留在原始外部元素上)-因此外部元素的宽度/高度来自内在等
从外部和内部元素中删除默认焦点样式:
.btn:焦点,
.btn__content:焦点{
大纲:none;
}
将焦点样式添加回内部元素仅当外部元素具有焦点时:
.btn:焦点 > .btn__content {
框阴影:0 0 2px 2px #51a7e8; /* 仅键盘焦点样式 /
颜色:石灰; / 仅限键盘的焦点样式 */
}
为什么这样做?
这里的技巧是用 tabindex="-1"
设置内部元素 - 参见 MDN:
A negative value (usually tabindex="-1" means that the element should
be focusable, but should not be reachable via sequential keyboard
navigation...
所以元素是 focusable 通过鼠标点击或编程,但另一方面 - 它不能通过键盘到达 'tabs'.
因此,当单击交互元素时 - 内部元素 获得焦点。不会显示焦点样式,因为我们已将其删除。
.btn:focus,
.btn__content:focus {
outline: none;
}
请注意,在给定时间只能关注 1 个 DOM 元素(以及 document.activeElement
returns 这个元素)- 所以 只有内部元素会被聚焦。
另一方面:当我们使用键盘进行 Tab 时 - 只有外部元素会获得焦点(记住:内部元素有 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 */
}
当然,我们要确保当我们按 Tab 键并按 enter
- 我们没有破坏我们的交互元素,javascript 将 运行.
这里有一个演示,说明情况确实如此,但请注意,尽管您只能免费获得此功能(即按 Enter 键以引发点击事件),但对于按钮和链接等固有的交互元素...对于其他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
注意:
- 尽管这似乎是一个过于复杂的解决方案,但对于非 javascript 解决方案而言,它实际上非常令人印象深刻。更简单的 css-only 'solutions' 涉及
:hover
和 :active
伪 class 样式根本不起作用。 (当然,除非您假设交互式元素会像模式说中的按钮一样在单击时立即消失)
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
此解决方案并不完美:windows 上的 firefox 仍会在单击时获得按钮的焦点样式 - 但这似乎是一个 firefox 错误(请参阅 the article)
当浏览器实现 :focus-ring pseudo class - there may be a much simpler solution to this problem - (see the article)
对于它的价值,有 a polyfill for :focus-ring
- see this article by Chris DeMars
仅键盘焦点样式的实用替代方案
因此实现仅键盘焦点样式非常困难。一种更简单并且可能既满足设计者的期望又易于访问的替代/解决方法是像悬停样式一样设置焦点样式。
Codepen
因此,虽然从技术上讲,这并没有实现纯键盘样式,但它实际上消除了对纯键盘样式的需求。
在尝试 Danield 接受的解决方案时,我找到了一种基于 inner/outer 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>
**保持尺寸和定位,使内部元素与外部元素完全重叠。将带有样式的整个 "button" 定位在外部元素上。
这是如何工作的:
当用户点击 "button" 时,他们点击的是移除了焦点轮廓的内部元素。无法单击外部元素,因为它被内部元素覆盖。当用户使用键盘切换到 "button" 时,他们会到达外部元素(tabindex="0" 使元素可通过 'tab' 访问),该元素获得焦点轮廓,但内部元素是无法通过选项卡访问(使用 tabindex="-1")并且在单击时不会收到焦点轮廓。
删除 outline
对可访问性来说太糟糕了!理想情况下,焦点环仅在用户 打算使用键盘时显示 。
2018答案:使用:focus-visible. It's currently a W3C proposal for styling keyboard-only focus using CSS. Until major browsers support it, you can use this robust polyfill。它不需要添加额外的元素或更改 tabindex
.
/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
outline: none;
}
/* Optional: Customize .focus-visible */
.focus-visible {
outline-color: lightgreen;
}
我还写了一篇更详细的文章 post 以防您需要更多信息。
没有明确的解决方案。
我做了一个 Hackish 解决方案:
在您的主容器上应用点击事件并在点击时编写以下代码
_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");
}
案例研究:Facebook 登录页面
Facebook 现在(2018 年 6 月)在他们的登录页面上使用了一小部分 Javascript。
Javascript 检测用户何时单击鼠标或使用键盘,并在 body 上打开和关闭 class:<body class="using-mouse">
然后 CSS 规则可以使用 class 来显示或隐藏相关元素上的适当焦点样式。
这是一些示例代码(也可用 on CodePen)。比较点击和 Tab 键。
// 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;
}
但这是一种妥协。它传达了鼠标用户并不真正感兴趣的焦点信息,对于键盘用户来说可能有点太微妙。
不过,这种折衷可能比任何一个极端(对所有用户都有一个强大纲,或者根本没有大纲)要好。
Whosebug 的主要按钮使用与 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
状态在点击时触发,因为鼠标必须悬停在元素上才能点击它。
2020 年更新
:focus-visible
已稳定 Chrome。只需使用它!仅缺少对 IE 和 Safari 的支持,因此添加后备方案(如下)。
需要一个很好的功能查询来区分 IE11 + Safari 和其他浏览器?这是一个 SCSS mixin:
@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>
将始终获得焦点。
原创POST
直到 :focus-visible
未出现在所有流行的常青浏览器中,您可以在 CSS 的全局部分使用这个简单的技巧,无需任何 polyfill:
@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。
我觉得这篇文章很有用,想分享它https://css-tricks.com/keyboard-only-focus-styles/
SCSS 中的示例:
button {
&:focus-visible {
border: 2px solid #004EA3;
}
}
正如一些人所说,:focus-visible
是纯 CSS 解决方案的方法。我想提供我仅使用 CSS 解决此样式问题的最简单方法,但是它在浏览器支持方面有一些缺点并且对所有人都没有用:
@supports not selector(:focus-visible) {
:focus {
// Fallback styles for browsers that doesn't support :focus-visible.
}
}
:focus-visible {
// Tab focus styles
}
仅在确实需要防止干扰 :focus-visible
样式时应用 :focus
。 :focus-visible
将被不支持它的浏览器简单地忽略,因此不需要 @supports
或类似的东西。
详细了解 @supports selector and :focus-visible 的浏览器支持。
如果你碰巧像我一样在 JS 中使用 CSS,你可以利用 CSS.supports() 有条件地呈现回退 CSS 以防你需要关心不支持的浏览器'支持@supports selector
。类似于 if (CSS.supports("selector(:focus-visible)")) {...}
.
我想在不需要时禁用 :focus
,因为我不喜欢焦点位于其上时导航的外观。它使用与 .active
相同的样式并且令人困惑。但是我不想为使用键盘的人摆脱它。
我想在按下 Tab 键时在正文中添加一个 class enabled-focus
然后添加 body.enabled-focus a:focus{...}
但这会为每个添加很多额外的 CSS具有焦点的元素。然后在第一次按下鼠标时从正文中删除 class。
我该怎么做?有更好的解决方案吗?
这个问题你可能会经常遇到。这类问题的好处是,一旦你找到了解决办法,就不会再困扰你了。
最优雅的解决方案似乎是最简单的:不要删除 :focus 上的大纲,而是在 :active 上删除 - 毕竟,:active 是动态伪 class 显式处理单击或以其他方式激活可聚焦元素时应应用的样式。
a:hover, a:active { outline: none; }
此方法唯一的小问题:如果用户激活 link 然后使用浏览器的后退按钮,轮廓就会变得可见。哦,众所周知,旧版本的 Internet Explorer 会混淆 :focus、:hover 和 :active 的确切含义,因此此方法在 IE6 及以下版本中失败。
提示
有一个简单的解决方法可以通过添加一个简单的 overflow:hidden
来防止轮廓“溢出”,它可以检查元素本身可点击部分周围的轮廓。
更新:这个问题可能不再相关
:focus-visible
pseudo class - which now has decent browser support...
我想补充一点,基于 spec which covers the :focus-visible pseudo class,浏览器现在应该仅在对用户有帮助时 指示焦点 - 例如在以下情况下用户通过键盘或其他一些非指点设备与页面交互
这基本上意味着原始问题不再相关,因为现在,当用户 clicks/taps 一个按钮(或另一个可聚焦元素)时,用户代理将不再显示聚焦环 - 甚至尽管按钮已聚焦 - 因为在这种情况下聚焦环对用户没有帮助。
来自 the spec:
While the :focus pseudo-class always matches the currently-focused element, UAs only sometimes visibly indicate focus (such as by drawing a “focus ring”), instead using a variety of heuristics to visibly indicate the focus only when it would be most helpful to the user. The :focus-visible pseudo-class matches a focused element in these situations only...
事实上,从版本 90 开始,Chromium 的用户代理样式表从 :focus
切换到 :focus-visible,并且由于这一变化,按钮点击不再调用焦点环
此外,从 version 87 开始,Firefox 还在其用户代理样式中使用 :focus-visible。
综上所述,如果需要自定义焦点样式,因为焦点样式现在已从 :focus
转移到 :focus-visible
,当使用自定义焦点样式覆盖默认样式时 - :focus-visible
应该使用伪 class。
像这样:
button:focus-visible {
/* remove default focus style */
outline: none;
/* custom focus styles */
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}
向后兼容性:
像这样使用 :focus-visible 的可能问题是,不支持 :focus-visible
的浏览器将显示默认的焦点环,它可能不清晰或不可见 - 取决于设计.
Šime Vidas,在 this article 中描述了当前使用 :focus-visible 伪 class 的可行策略 - 即使在尚不支持 :focus-visible 的浏览器中也可以使用-
A good way to start using :focus-visible today is to define the focus styles in a :focus rule and then immediately undo these same styles in a :focus:not(:focus-visible) rule. This is admittedly not the most elegant and intuitive pattern, but it works well in all browsers:
Browsers that don’t support :focus-visible use the focus styles defined in the :focus rule and ignore the second style rule completely (because :focus-visible is unknown to them).
In browsers that do support :focus-visible, the second style rule reverts the focus styles defined in the :focus rule if the :focus-visible state isn’t active as well. In other words, the focus styles defined in the :focus rule are only in effect when :focus-visible is also active.
button:focus {
outline: none;
background: #ffdd00; /* gold */
}
button:focus:not(:focus-visible) {
background: white; /* undo gold */
}
原答案:
This excellent article by Roman Komarov 提出了实现 仅键盘焦点样式 用于 按钮 、 链接的可行解决方案 和其他容器元素,例如 spans 或 divs(通过 tabindex 属性人为地使其可聚焦)
解决方案:
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
- 用
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>
将 css 样式移动到内部元素(布局 css 应保留在原始外部元素上)-因此外部元素的宽度/高度来自内在等
从外部和内部元素中删除默认焦点样式:
.btn:焦点, .btn__content:焦点{ 大纲:none; }
将焦点样式添加回内部元素仅当外部元素具有焦点时:
.btn:焦点 > .btn__content { 框阴影:0 0 2px 2px #51a7e8; /* 仅键盘焦点样式 / 颜色:石灰; / 仅限键盘的焦点样式 */ }
为什么这样做?
这里的技巧是用 tabindex="-1"
设置内部元素 - 参见 MDN:
A negative value (usually tabindex="-1" means that the element should be focusable, but should not be reachable via sequential keyboard navigation...
所以元素是 focusable 通过鼠标点击或编程,但另一方面 - 它不能通过键盘到达 'tabs'.
因此,当单击交互元素时 - 内部元素 获得焦点。不会显示焦点样式,因为我们已将其删除。
.btn:focus,
.btn__content:focus {
outline: none;
}
请注意,在给定时间只能关注 1 个 DOM 元素(以及 document.activeElement
returns 这个元素)- 所以 只有内部元素会被聚焦。
另一方面:当我们使用键盘进行 Tab 时 - 只有外部元素会获得焦点(记住:内部元素有 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 */
}
当然,我们要确保当我们按 Tab 键并按 enter
- 我们没有破坏我们的交互元素,javascript 将 运行.
这里有一个演示,说明情况确实如此,但请注意,尽管您只能免费获得此功能(即按 Enter 键以引发点击事件),但对于按钮和链接等固有的交互元素...对于其他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
注意:
- 尽管这似乎是一个过于复杂的解决方案,但对于非 javascript 解决方案而言,它实际上非常令人印象深刻。更简单的 css-only 'solutions' 涉及
:hover
和:active
伪 class 样式根本不起作用。 (当然,除非您假设交互式元素会像模式说中的按钮一样在单击时立即消失)
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
此解决方案并不完美:windows 上的 firefox 仍会在单击时获得按钮的焦点样式 - 但这似乎是一个 firefox 错误(请参阅 the article)
当浏览器实现 :focus-ring pseudo class - there may be a much simpler solution to this problem - (see the article) 对于它的价值,有 a polyfill for
:focus-ring
- see this article by Chris DeMars
仅键盘焦点样式的实用替代方案
因此实现仅键盘焦点样式非常困难。一种更简单并且可能既满足设计者的期望又易于访问的替代/解决方法是像悬停样式一样设置焦点样式。
Codepen
因此,虽然从技术上讲,这并没有实现纯键盘样式,但它实际上消除了对纯键盘样式的需求。
在尝试 Danield 接受的解决方案时,我找到了一种基于 inner/outer 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>
**保持尺寸和定位,使内部元素与外部元素完全重叠。将带有样式的整个 "button" 定位在外部元素上。
这是如何工作的:
当用户点击 "button" 时,他们点击的是移除了焦点轮廓的内部元素。无法单击外部元素,因为它被内部元素覆盖。当用户使用键盘切换到 "button" 时,他们会到达外部元素(tabindex="0" 使元素可通过 'tab' 访问),该元素获得焦点轮廓,但内部元素是无法通过选项卡访问(使用 tabindex="-1")并且在单击时不会收到焦点轮廓。
删除 outline
对可访问性来说太糟糕了!理想情况下,焦点环仅在用户 打算使用键盘时显示 。
2018答案:使用:focus-visible. It's currently a W3C proposal for styling keyboard-only focus using CSS. Until major browsers support it, you can use this robust polyfill。它不需要添加额外的元素或更改 tabindex
.
/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
outline: none;
}
/* Optional: Customize .focus-visible */
.focus-visible {
outline-color: lightgreen;
}
我还写了一篇更详细的文章 post 以防您需要更多信息。
没有明确的解决方案。 我做了一个 Hackish 解决方案: 在您的主容器上应用点击事件并在点击时编写以下代码
_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");
}
案例研究:Facebook 登录页面
Facebook 现在(2018 年 6 月)在他们的登录页面上使用了一小部分 Javascript。
Javascript 检测用户何时单击鼠标或使用键盘,并在 body 上打开和关闭 class:<body class="using-mouse">
然后 CSS 规则可以使用 class 来显示或隐藏相关元素上的适当焦点样式。
这是一些示例代码(也可用 on CodePen)。比较点击和 Tab 键。
// 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;
}
但这是一种妥协。它传达了鼠标用户并不真正感兴趣的焦点信息,对于键盘用户来说可能有点太微妙。
不过,这种折衷可能比任何一个极端(对所有用户都有一个强大纲,或者根本没有大纲)要好。
Whosebug 的主要按钮使用与 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
状态在点击时触发,因为鼠标必须悬停在元素上才能点击它。
2020 年更新
:focus-visible
已稳定 Chrome。只需使用它!仅缺少对 IE 和 Safari 的支持,因此添加后备方案(如下)。
需要一个很好的功能查询来区分 IE11 + Safari 和其他浏览器?这是一个 SCSS mixin:
@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>
将始终获得焦点。
原创POST
直到 :focus-visible
未出现在所有流行的常青浏览器中,您可以在 CSS 的全局部分使用这个简单的技巧,无需任何 polyfill:
@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。
我觉得这篇文章很有用,想分享它https://css-tricks.com/keyboard-only-focus-styles/
SCSS 中的示例:
button {
&:focus-visible {
border: 2px solid #004EA3;
}
}
正如一些人所说,:focus-visible
是纯 CSS 解决方案的方法。我想提供我仅使用 CSS 解决此样式问题的最简单方法,但是它在浏览器支持方面有一些缺点并且对所有人都没有用:
@supports not selector(:focus-visible) {
:focus {
// Fallback styles for browsers that doesn't support :focus-visible.
}
}
:focus-visible {
// Tab focus styles
}
仅在确实需要防止干扰 :focus-visible
样式时应用 :focus
。 :focus-visible
将被不支持它的浏览器简单地忽略,因此不需要 @supports
或类似的东西。
详细了解 @supports selector and :focus-visible 的浏览器支持。
如果你碰巧像我一样在 JS 中使用 CSS,你可以利用 CSS.supports() 有条件地呈现回退 CSS 以防你需要关心不支持的浏览器'支持@supports selector
。类似于 if (CSS.supports("selector(:focus-visible)")) {...}
.