【问题标题】:tabIndex doesn't make a label focusable using Tab keytabIndex 不会使用 Tab 键使标签成为焦点
【发布时间】:2014-12-18 15:44:28
【问题描述】:

我正在尝试用图标替换复选框/收音机输入。为此,我需要隐藏原始复选框/收音机。问题是,我还希望表单能够正确支持键盘输入,即让输入通过Tab 键保持焦点并使用Spacebar 进行选择。由于我隐藏了输入,因此无法聚焦,因此,我试图使其 <label> 可聚焦。

This documentation 和其他各种来源让我相信我可以使用tabindex 属性(对应于HTMLElement.tabIndex 属性)来做到这一点。但是,当我尝试将 tabindex 分配给我的标签时,它仍然像以往一样没有重点,无论我如何尝试将 Tab 分配给它。

为什么tabindex 不让标签成为焦点?

以下 sn-p 演示了该问题。如果您用鼠标聚焦输入并尝试使用Tab 聚焦标签,它不起作用(它会用tabindex 聚焦下面的<span>)。

document.getElementById('checkbox').addEventListener('change', function (event) {
  document.getElementById('val').innerHTML = event.target.checked;
});
<div>
  <input type="text" value="input">
</div>
<div>
  <label tabindex="0">
    <input type="checkbox" id="checkbox" style="display:none;">
    checkbox: <span id="val">false</span>
  </label>
</div>
<span tabindex="0">span with tabindex</span>

(JavaScript 代码只允许看到正确单击标签(取消)选中复选框。)

【问题讨论】:

  • 标签不是层,也不是输入,它只是一个符号元素
  • @UnskilledFreak 为什么这会让tabindex 不起作用?我在代码中添加了&lt;span&gt; 以更好地演示该问题。
  • tabindex 在不使用鼠标的情况下从输入跳转到输入是“史前”的,所以你为什么要跳转到标签?什么原因?
  • tabindex 今天对于可访问性来说仍然非常重要。此外,它仍然是part of the HTML specification。最后,对于 label 元素,tabindexa valid attribute

标签: html


【解决方案1】:

为什么 tabindex 不能使标签成为焦点?

简答

  1. 标签可聚焦。
  2. TabIndex 不会有任何区别。
  3. 欢迎来到浏览器/代理不一致的世界。

tl;dr;

label (Ref) 元素非常具有焦点。它的 DOM 接口是 HTMLLabelElement,它派生自 HTMLElement (Ref),后者又实现了 GlobalEventHandlers (Ref),因此公开了 focus() 方法和 onfocus 事件处理程序。

您无法获得labels 焦点行为的正确规范/参考文档的原因是因为您可能一直在查看 HTML5 规范。有趣的是,HTML5 refs 没有说明任何与此相关的内容,这增加了混乱。

这在 HTML 4.01 参考这里提到:http://www.w3.org/TR/html401/interact/forms.html#h-17.9.1

特别是在第 17.9.1 节的末尾和 17.10 之前:

当一个 LABEL 元素获得焦点时,它将焦点传递给它的 关联控件。

另外,在其他地方(我无法掌握参考的那部分)我已经读到它取决于执行代理。 (不要相信我的话,我不太确定)。

但是,这意味着当您 focuslabel(或 label 收到 focus)时,focus 将传递给其关联的可标记控件。这不会导致两个不同的focuses,而是input 上的一个focus(在您的情况下是一个复选框)。由于这种行为,tabindex 属性无法发挥作用。

这里还有 W3C 的网站可访问性 (WAAG) 测试套件:http://www.w3.org/WAI/UA/TS/html401/cp0102/0102-ONFOCUS-ONBLUR-LABEL.html,其中讨论了 onfocusonblur 的实现 label。理想情况下,键盘或模拟键盘的辅助技术应该实现这一点。但是……

这就是浏览器不一致发挥作用的地方。

这个例子可以证明这一点。在不同的浏览器中检查以下sn -p。 (我已经针对 IE-11、GC-39 和 FF-34 对其进行了测试。它们的行为都不同。)

  1. 点击按钮“焦点标签
  2. 它应该聚焦标签,然后传递焦点并将其关联的复选框轮廓突出显示为蓝色。
  3. Chrome-v39 有效。 IE-v11 它没有(不知何故 html 和 body 确实响应 :focus)。 FF-v34 有效。

谈到浏览器的不一致,请尝试使用“访问密钥”L。有些浏览器会关注复选框,而有些浏览器会点击它,即将动作传递给它。

这是一个 fiddle 来测试它:http://jsfiddle.net/abhitalks/ff0xds4z/2/

这是一个sn-p

label = $("label").first();
$("#btn").on("click", function() {
    label.focus();
});
* { margin: 8px; }
.highlight { background-color: yellow; }
:focus {
    outline: 2px solid blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="txt" type="text" value="input" /><br />
<label for="chk" accesskey="L">Checkbox: </label>
<input id="chk" type="checkbox" /><br />
<input id="btn" type="button" value="Focus Label" />

希望能解开你的疑惑。

.


你的问题:

现在focussing (sic) 解决您最初无法聚焦标签的问题,因为您想通过在其位置放置一个图标类型的东西来以不同的方式设置复选框的样式。

为了做到这一点,您可以选择通过display:none; 将其完全隐藏。相反,将其设为 1x1 像素并将其推到您的图标下方。这样它仍然会自然地获得焦点,但实际上会被隐藏。

例如,如果你的图标是一个复选标记和一个十字,那么改变复选框的位置,使图标脱离::before::after标签上的伪元素。这将导致复选框仍然获得焦点,并使图标对此做出响应。这将给出图标占据焦点的明显错觉。

演示小提琴:http://jsfiddle.net/abhitalks/v0vxcw77/

片段

div.chkGroup { position: relative; }
input#chk {
    position: absolute;
    width: 1px; height: 1px;
    margin: 0; margin-top: 4px; outline: none;
    border: 1px solid transparent; background-color: transparent;
}
label::before {
    content: '\2714';
    position: relative;
    width: 18px; height: 18px;
    background-color: #fff;
    margin-right: 8px; padding: 2px;
    display: inline-block; 
    border: 1px solid transparent;
}
input#chk:checked + label::before {
    content: '\2716';
}
input#chk:focus + label::before {
    border: 1px solid #00f;
}
<input id="txt" type="text" value="input" /><br /><br />
<div class="chkGroup">
    <input id="chk" type="checkbox" />
    <label for="chk" accesskey="L">Checkbox</label>
</div>

.

【讨论】:

  • 我怀疑标签会将焦点转移到输入,但不确定。感谢您为我指明正确的方向。也感谢建议的解决方案,但我将采用不同的策略。您的解决方案需要在图标后面放置一个不透明的背景,这非常不方便。相反,我会在复选框后自动添加一个“替换”。使用tabindex 和一些脚本,我能够将适当的事件传输到输入(在其上发送它们)。由于我使用的是 AngularJS,因此很容易将其封装在 input 指令中。
  • @hon2a:是的。这也是一个非常简洁的解决方案。您可以在复选框本身上放置一个替换并处理它。最良好的祝愿。
  • 只是想知道,当按下 Alt+Shift+l 时,浏览器/平台没有选中第一个 sn-p 中的复选框的示例是什么?
  • 再次关于第一个 sn-p,看起来 IE-11.0.8 正在关注Focus Management(截至 2014 年 12 月 19 日)。将tabindex 添加到&lt;label/&gt; 会导致标签满足条件,并且按钮确实 会导致焦点发生变化;-)。也许 HTML5 草案中没有焦点链接,因为行为取决于实现,但看起来 IE 尊重草案......
【解决方案2】:

编辑:以下是对规范的误读:

看着the full specification, 你会看到有一个叫做tabindex focus flag的东西, 它定义了 tabindex 属性是否真的会生成该字段 “可选项”。该建议列表中缺少 label 元素 元素。

但话说回来,span 元素也是如此,所以请看图 :)。

也就是说,y您可以通过将整个内容包装在另一个元素中来使标签文本具有焦点,或者使用一些 JavaScript 来强制执行该问题。不幸的是,包装(这里是锚点)可以让人们在 CSS 和 JS 中进行大量额外的工作,才能像普通的 label 元素一样工作。

document.getElementById('checkbox').addEventListener('change', function(event) {
  document.getElementById('val').innerHTML = event.target.checked;
});
document.getElementsByClassName('label')[0].addEventListener('click', function(event) {
  event.target.getElementsByTagName('label')[0].click();
  event.preventDefault();
});
document.getElementsByClassName('label')[0].addEventListener('keypress', function(event) {
  if ((event.key || event.which || event.keyCode) === 32) {
    event.target.getElementsByTagName('label')[0].click();
    event.preventDefault();
  }
});
.label,
.label:visited,
.label:hover,
.label:active {
  text-decoration: none;
  color: black;
}
<div>
  <input type="text" value="input">
</div>
<div>
  <a class="label" href="#">
    <label tabindex="0">
      <input type="checkbox" id="checkbox" style="display:none;">checkbox: <span id="val">false</span>
    </label>
  </a>
</div>
<span tabindex="0">span with tabindex</span>

【讨论】:

  • 您注意的规范部分列出了浏览器供应商默认设置焦点标志的元素(即:不需要tabindex 属性使其可聚焦)
  • 很抱歉之前没有阅读链接的文档。据我所知,@steveax 是对的。文档指出,某些元素应默认设置 tabindex 焦点标志label 不在其中),否则应在存在tabindex 属性的情况下设置该标志.所以,我不得不不接受这个答案。
  • @MikeMcCaughan 您可能需要编辑您的答案,因为开头的信息具有误导性。
【解决方案3】:

由于这篇旧帖子是 html label tabindex 的顶级谷歌搜索结果之一,我想添加我非常简单的工作解决方案。正如@Abhitalks 在接受的答案中提到的那样,标签的焦点被传递给它的关联控件。所以要绕过这种行为,只需在标签中添加tabindex 并在focus EventListener 中使用event.preventDefault()

@Heretic Monkey 在他的回答中有点正确的想法,但您不需要包装器元素来实现这一点。但是,您需要手动转发任何所需的击键(如空格键)。

例如:

'use strict';

let field = document.getElementById('hidden-file-chooser');
let label = document.querySelector('label[for=hidden-file-chooser]');

// prevent focus passing
label.addEventListener('focus', event => {
  event.preventDefault();
});

// activate using spacebar
label.addEventListener('keyup', event => {
  if (event.keyCode == 32) {
    field.click();
  }
});
#hidden-file-chooser {
  display: none;
}

input[type=text] {
  display: block;
  width: 20rem;
  padding: 0.5rem;
}

label[for=hidden-file-chooser] {
  display: inline-block;
  background: deepskyblue;
  margin: 1rem;
  padding: 0.5rem 1rem;
  border: 0;
  border-radius: 0.2rem;
  box-shadow: 0 0 0.5rem 0 rgba(0,0,0,0.7);
  cursor: pointer;
}
<input type="text" placeholder="Click here and start tabbing through ...">

<input id="hidden-file-chooser" type="file">
<label for="hidden-file-chooser" tabindex="0"> Select a File </label>

<input type="text" placeholder="... then shift+tab to go back.">

P.S:我在示例中使用了input[type=file],因为这是我遇到此问题时正在处理的问题。相同的原则适用于任何输入类型。

【讨论】:

  • 只是想发表评论,特别感谢@Besworks 的回答。正是我需要的!我唯一要弄清楚的是如何防止它在输入时仍然停留在隐藏的复选框上。但至少我的标签(样式看起来像菜单按钮)现在可以正确显示焦点样式了!
  • 更新:将tabstop="-1" 添加到复选框可防止其被关注。这样它在输入时通过选项卡传递。还注意到空格键会滚动页面,因此我在跟踪空格键输入的 keydown(不是 keyup)事件中添加了一个 e.preventDefault()。我还为回车键添加了一个键(keyCode == 13)。现在一切正常!
【解决方案4】:

正如之前的海报所说: 标签焦点始终直接指向输入元素。

如果有人有花哨的(但假的)复选框,隐藏原始的复选框,而键盘导航的实际焦点无处可寻,那就太烦人了。

我能想到的最佳解决方案:javascript。

风格-远离实际焦点,有利于虚假焦点:

    input[type=checkbox]:focus {
        outline: none;
    }
    .pseudo-focus {
        outline: 2px solid blue;
    }

并注意(在许多情况下明显隐藏的)原始复选框的更改:

    $('input[type=checkbox')
        .focus( function() {
            $(this).closest('label').addClass('pseudo-focus');
        })
        .blur( function() {
            $(this).closest('label').removeClass('pseudo-focus');    
        });

完整的jsfiddle here

【讨论】:

  • 通过常规方式隐藏的输入(如display: none)无法获得焦点。您的 JSFiddle 实际上并没有隐藏任何东西,因此也没有显示出来。
  • 我纠正了小提琴,不知何故弄错了版本。现在它显示,您也可以通过标签虚拟地进行选项卡。 |没错,输入不能被隐藏,只能通过left:-999px;position: absolute; 或类似的方式可见。
【解决方案5】:

对于输入类型单选或复选框:

opacity: 0;
height: 0;
width: 0;
min-height: 0;
line-height: 0;
margin: 0;
padding: 0;
border: 0 none;

上面的 Js 做得很好。

【讨论】:

    猜你喜欢
    • 2010-11-09
    • 1970-01-01
    • 2010-10-06
    • 2012-05-16
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多