启用:仅关注键盘使用(或按键)

时间:2015-07-14 09:19:23

标签: javascript css focus

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

我正在考虑在按Tab键的主体上添加一个类enabled-focus,然后使用body.enabled-focus a:focus{...},但这会为每个具有焦点的元素添加大量额外的CSS。然后在第一次鼠标按下时从主体中删除该类。

我该怎么办?有更好的解决方案吗?

9 个答案:

答案 0 :(得分:38)

This excellent article 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;
}
&#13;
<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>
&#13;
&#13;
&#13;

Codepen

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 =&#34; -1&#34;表示元素应该   是可聚焦的,但不应通过顺序键盘访问   导航...

因此,通过鼠标点击或编程方式,该元素可聚焦,但另一方面 - 它无法通过键盘选项卡达到。&#39;。

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

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

请注意,在给定时间只能聚焦一个DOM元素document.activeElement会返回此元素) - 因此内部元素将被聚焦

另一方面:当我们使用键盘选项卡时 - 只有外部元素才能获得焦点(请记住:内部元素有tabindex =&#34; -1&#34;并且isn& #39; t可通过顺序键盘导航访问)[请注意,对于固有的不可聚焦的外部元素,如可点击的<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就会运行。

这是一个演示,表明确实是这种情况,请注意,尽管你只是免费获得这个(例如按Enter键导致点击事件),对于固有的交互式元素,如按钮和链接......对于其他元素,如作为跨度 - 您需要手动编写代码:)

&#13;
&#13;
//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);
});
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

Codepen

<强> NB:

1)虽然这看起来像是一个过于复杂的解决方案,但对于非JavaScript解决方案来说,它确实令人印象深刻。更简单的css-only&#39;解决方案&#39;涉及:hover:active伪类样式的工作根本不起作用。 (当然,除非您假设交互式元素在点击时立即消失,如模态中的按钮所示)

&#13;
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

Codepen

2)这个解决方案并不完美:windows上的firefox仍会获得点击按钮的焦点样式 - 但这似乎是一个firefox bug(参见the article

3)当浏览器实现:fo­cus-ring伪类时 - 可能会有一个更简单的解决方案来解决这个问题 - (参见the article) 对于它的价值,:focus-ringa polyfill - 请参阅this article by Chris DeMars

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

因此,实现仅键盘式对焦样式非常困难。一个替代/解决方案更简单并且既可以满足设计师的期望并且也可以访问 - 将会像你悬停时的样式那样定制焦点。

Codepen

因此,虽然从技术上讲这不是实现仅限键盘的样式,但它基本上不再需要仅限键盘的样式。

答案 1 :(得分:20)

案例研究:Facebook登录页面

Facebook现在(2018年6月)在其登录页面上使用了少量Javascript。

JavaScript会检测用户何时单击鼠标或使用键盘,并打开和关闭主体上的类:<body class="using-mouse">

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

以下是一些示例代码(on CodePen也可用):

// Let the document know when the mouse is being used
document.body.addEventListener('mousedown', function() {
  document.body.classList.add('using-mouse');
});
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. */
/* You could apply this selector only to buttons, if you wanted. */
body.using-mouse :focus {
  outline: none;
}
<input>
<button>Submit</button>


案例研究:GMail登录页面

或者,无论用户是用鼠标还是用键盘,GMail都只是用比未聚焦按钮更重的阴影来设计聚焦按钮的样式。

这是一致的,易于实现和测试,并且不需要Javascript。

但这是一个折衷。它传达了鼠标用户并不真正感兴趣的焦点信息,并且对于键盘用户来说,它可能有些微不足道。

尽管如此,这种折衷方案还是要比其中一种极端情况好得多(到处都有很强的轮廓,或者根本没有轮廓)。

答案 2 :(得分:12)

这是一个你可能会遇到的问题。这些问题的好处是,如果你曾经找到解决方案,它就不会再打扰你了。

最优雅的解决方案似乎是最简单的:不要删除大纲:焦点,执行它:激活而不是 - 毕竟,:active是动态伪类,它明确地处理应该是的样式单击或以其他方式激活可聚焦元素时应用。

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

此方法的唯一小问题:如果用户激活链接然后使用浏览器的后退按钮,则轮廓变为可见。哦,旧版本的Internet Explorer臭名昭着的确切含义混淆了:focus,:hover和:active,所以这个方法在IE6及以下版本中失败。

<强>蒂普

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

答案 3 :(得分:12)

删除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;
}

我还写了一个更详细的post,以防您需要更多信息。

答案 4 :(得分:3)

在玩Danield公认的解决方案的过程中,我发现了一种基于内/外div概念的替代方法。

1)创建外部和内部元素。给外部元素tabindex =&#34; 0&#34;和内部元素tabindex =&#34; -1&#34;

<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>

**保持尺寸和位置,使内部元素与外部元素完全重叠。定位整个&#34;按钮&#34;在外部元素上使用样式。

这是如何运作的:

当用户点击&#34;按钮&#34;时,他们点击删除了焦点轮廓的内部元素。由于外部元素被内部元素覆盖,因此无法单击外部元素。当用户使用键盘选项卡到&#34;按钮&#34;时,它们会到达外部元素(tabindex =&#34; 0&#34;使元素可以通过&#39; tab&#39;来访问)获取焦点轮廓,但内部元素无法通过选项卡访问(使用tabindex =&#34; -1&#34;)并且在单击时不会获得焦点轮廓。

答案 5 :(得分:0)

没有明确的解决方案。 我做了一个Hackish解决方案: 在您的主容器上应用click事件,并在单击

上写下代码
    _handleMouseClick = (event) => {
        if(event.detail){
            document.activeElement.blur();
        }
    }

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

OR

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

答案 6 :(得分:0)

&:focus:not(:hover) { }

它不能在100%的情况下起作用,但我认为对于大多数人来说这应该足够了。

这将防止单击时触发:focus状态,因为鼠标必须悬停(悬停)元素才能单击它。

https://codepen.io/heyvian/pen/eopOxr

答案 7 :(得分:0)

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

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

但是,我还不知道他们如何管理它。

答案 8 :(得分:0)

正如其他人提到的,: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;
  }
}