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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31402576/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:40:29  来源:igfitidea点击:

Enable :focus only on keyboard use (or tab press)

javascriptcssfocus

提问by Miro

I want to disable :focuswhen it's not needed because I don't like how my navigation looks when the focus is on it. It uses the same style as .activeand it's confusing. However I don't want to get rid of it for people who use keyboard.

我想在:focus不需要的时候禁用它,因为我不喜欢我的导航在焦点上的样子。它使用相同的风格,.active令人困惑。但是,我不想为使用键盘的人摆脱它。

I was thinking to add a class enabled-focuson the body on tab press and then have body.enabled-focus a:focus{...}but that would add a lot of extra CSS for every element that has focus. Then remove that class from the body on first mouse down.

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

How would I go about it? Is there a better solution?

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

回答by Danield

This excellent articleby Roman Komarovposes a viable solution for achieving keyboard-only focus stylesfor buttons, linksand other container elements such as spansor divs(which are artificially made focusable with the tabindex attribute)

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

The Solution:

解决方案:

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) Wrap the content of the original interactive element inside an additional inner element with tabindex="-1"(see explanation below)

1)将原始交互元素的内容包裹在一个额外的内部元素中tabindex="-1"(见下面的解释)

So instead of say:

所以,而不是说:

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

2) Move the css styling to the inner element (layout css should remain on the original outer element) - so the width / height of the outer element come from the inner one etc.

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

3) Remove default focus styling from both outer and inner elements:

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

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

4) Add focus styling back to the inner element only whenthe outer element has focus:

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

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

Why does this work?

为什么这样做?

The trick here is setting the inner element with tabindex="-1"- see MDN:

这里的技巧是设置内部元素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...

负值(通常 tabindex="-1" 意味着该元素应该是可聚焦的,但不应通过顺序键盘导航访问...

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

因此该元素可以通过鼠标点击或以编程方式获得焦点,但另一方面 - 它无法通过键盘“标签”访问。

So when the interactive element is clicked - the inner elementgets the focus. No focus styles will show because we have removed them.

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

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

Note that only 1 DOM element can be focused at a given time(and document.activeElementreturns this element) - so onlythe inner element will be focused.

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

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

另一方面:当我们使用键盘 Tab 键时 -只有外部元素会获得焦点(请记住:内部元素具有 tabindex="-1" 并且无法通过顺序键盘导航访问)[请注意,对于固有的非-可聚焦的外部元素,如可点击<div>- 我们必须通过添加tabindex="0"]来人为地使它们可聚焦

Now our CSS kicks in and adds the keyboard-only focus styles to the inner element.

现在我们的 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 */
} 

Of course, we want to make sure that when we tab and press enter- we haven't broken our interactive element and the javascript will run.

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

Here is a demo to show that this is indeed the case, note though that you only get this for free (ie pressing enter to cause a click event) for inherently interactive elements like buttons and links... for other elements such as spans - you need to code that up manually :)

这是一个演示,表明情况确实如此,但请注意,对于按钮和链接等固有交互元素...你需要手动编码:)

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

代码笔



NB:

注意:

1) Although this seems like an overly-complicated solution, for a non-javascript solution it's actually quite impressive. Simpler css-only 'solutions' involving :hoverand :activepseudo class styling simply don't work. (unless of course you assume that the interactive element disappears immediately on click like a button within a modal say)

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

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

代码笔

2) This solution isn't perfect: firefox on windows will still get focus styles for buttons on click - but that seems to be a firefox bug (see the article)

2)这个解决方案并不完美:windows上的firefox仍然会在点击时获得按钮的焦点样式 - 但这似乎是一个firefox错误(参见文章

3) When browsers implement the :fo-cus-ringpseudo class - there may be a much simpler solution to this problem - (see the article) For what it's worth, there is a polyfillfor :focus-ring- see this article by Chris DeMars

3)当浏览器实现的:FO-CUS环伪类-有可能是一个更简单的解决了这个问题- (见文章),对于它的价值,有一个填充工具:focus-ring-看到这篇文章由克里斯·DeMars



A pragmatic alternative to keyboard-only focus styles

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

So achieving keyboard-only focus styles is surprisingly difficult. One alternative / workaround which is much simplerand may both fulfil the designer's expectations and also be accessible - would be to style focus just like you would style for hover.

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

Codepen

代码笔

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

因此,尽管从技术上讲这并没有实现纯键盘样式,但它基本上消除了对纯键盘样式的需求。

回答by joeytwiddle

Case study: Facebook login page

案例研究:Facebook 登录页面

Facebook is using a tiny bit of Javascript on their login page right now (June 2018).

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

The Javascript detects when the user has clicked their mouse or used their keyboard, and toggles a class on and off on the body: <body class="using-mouse">

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

Then CSS rules can use that class to show or hide the appropriate focus styling on the relevant elements.

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

Here is some example code (also available on CodePen). Compare clicking and tabbing.

这是一些示例代码(也可在 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>

Note that :focusabove is equivalent to *:focus, matching all elements. If you only wanted to remove styling from buttons, you could put button:focusthere instead.

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



Case study: GMail login page

案例研究:GMail 登录页面

Alternatively, at that time GMail was just styling focused buttons with a heavier shadow than unfocused buttons, regardless of whether the user was on mouse or keyboard.

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

This is simple to implement and understand, and doesn't require any Javascript.

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

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

But it's a compromise. It conveys focus information that mouse users aren't really interested in, and it might be a bit too subtlefor keyboard users.

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

Still, this compromise is probably better than either one of the extremes (a strong outline for all users, or no outline at all).

尽管如此,这种折衷方案可能比任何一个极端(对所有用户都具有强大的轮廓,或者根本没有轮廓)要好。



StackOverflow's primary buttons use a similar approach to GMail, but with a more stylised look:

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

Personally I would use a stronger (higher contrast) colour, for accessibility.

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

回答by Aaron Noel De Leon

Removing outlineis terrible for accessibility! Ideally, the focus ring shows up only when the user intends to use the keyboard.

删除outline对于可访问性来说很糟糕!理想情况下,对焦环仅在用户打算使用键盘时出现

2018 Answer:Use :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. It doesn't require adding extra elements or altering the tabindex.

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

I also wrote a more detailed postjust in case you need more info.

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

回答by Jeremy Zahner

This is a problem you will probably encounter a lot. The good thing about such problems is, if you once find a solution, it won't bother you any more.

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

The most elegant solution seems to be the simplest: don't remove the outline on :focus, do it on :active instead – after all, :active is the dynamic pseudo-class that deals explicitly with the styles that should be applied when a focusable element is clicked or otherwise activated.

最优雅的解决方案似乎是最简单的:不要删除 :focus 上的轮廓,而是在 :active 上删除 – 毕竟,:active 是动态伪类,它明确处理应在以下情况下应用的样式单击或以其他方式激活可聚焦元素。

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

The only minor issues with this method: if a user activates a link and then uses the browser's back button, the outline becomes visible. Oh, and old versions of Internet Explorer notoriously get confused by the exact meaning of :focus, :hover and :active, so this method fails in IE6 and below.

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

Tipp

蒂普

There is a trivial workaround to prevent outlines from “spilling over” by adding a simple overflow:hidden, which keeps the outline in check around the clickable portion of the element itself.

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

回答by nutsandbolts

In playing with the accepted solution by Danield, I found an alternative, simpler way based on the inner/outer div concept.

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

1) Create an outer and inner element. Give the outer element tabindex="0" and the inner element tabindex="-1"

1) 创建外部和内部元素。给外部元素 tabindex="0" 和内部元素 tabindex="-1"

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

2) In the css, remove outline from the inner element when focused:

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

.inner:focus{
    outline: none;
}

3) Apply any mouse or click event handlers to the inner element. Apply any focus events (onfocus, onblur, onkeydown) to the outer element.

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

For example:

例如:

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

**Maintain the size and positioning such that the inner element completely overlaps the outer element. Position the entire "button" with styling on the outer element.

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

How this works:

这是如何工作的:

When the user clicks on the "button", they are clicking on the inner element which has the focus outline removed. It is not possible to click on the outer element since it is covered by the inner element. When the user uses the keyboard to tab to the "button", they get to the outer element (tabindex="0" makes the element reachable with 'tab') which gets a focus outline, but the inner element is not reachable through the tab (with tabindex="-1") and does not receive focus outline when clicked.

当用户点击“按钮”时,他们点击的是移除了焦点轮廓的内部元素。无法单击外部元素,因为它被内部元素覆盖。当用户使用键盘 Tab 到“按钮”时,他们会到达外部元素(tabindex="0" 使元素可以通过 'tab' 到达),它获得焦点轮廓,但内部元素无法通过tab(带有 tabindex="-1")并且在单击时不接收焦点轮廓。

回答by Vian Esterhuizen

&:focus:not(:hover) { }

It won't work in 100% of cases but I think for most people's need this should be sufficient.

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

It will prevent the :focusstate being trigger on click because the mouse has to be over (hovering) the element to click it.

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

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

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

回答by Neurotransmitter

Until :focus-visibleis not there in all popular evergreen browsers, you can use this simple trick in the global part of your CSS, without any polyfills:

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

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

and then add focus effects as you normally do, with :focus.

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

At this point you probably learned, that setting the outline: none;to focused elements by default is a horrible idea from the accessibility point of view. That is certainly true.

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

However, if you scope this rule in the pointer: coarsemedia query, it becomes very useful, since it will only apply to mobile phones and tablets, but not desktops. Which is exactly what you want to achieve.

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

The only issue I can think about are mobile users with keyboards, which they use for tabbing through content, but I'm not sure if there is a lot of such users. So, ultimately :focus-visiblewill be the better solution, but for now this should be enough.

我能想到的唯一问题是带有键盘的移动用户,他们用它来浏览内容,但我不确定是否有很多这样的用户。所以,最终:focus-visible将是更好的解决方案,但现在这应该足够了。

回答by pareshm

There is no clear solution. I have done one Hackish solution : apply click event on your Main Container and write below code on click

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

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

When you click using mouse you will get event.detail = 1 on that click blur that element so that it will remove the outline and on keyboard click we get event.detail = 0 so in keyboard case behave normal

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

OR

或者

In css file

在 css 文件中

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

In Main js

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