防止某些元素获得焦点

27

我有以下函数。它会监听所有元素的焦点事件。如果该元素位于$mobileMenu$menuItems中,则允许它,否则将移除焦点:

var $body = $("body");
var $mobileMenu = $("#mobile-menu");
var $menuItems = $("#main-menu a");

$body.on("focus.spf", "*", function(e){
  e.stopPropagation();
  $this = $(this);

  // Prevent items from recieving focus and switching view
  if (!$this.is($mobileMenu) && !$this.is($menuItems)) {
    $this.blur();
  } else {
    console.log(this);
  }
})

我的问题是,如果一个本来可以被聚焦的元素现在变成了不可聚焦的,并且在我的白名单元素之前出现,那么用户就无法专注于任何事情,因为它会一遍又一遍地尝试重新聚焦在同一个元素上。

有没有人知道我如何告诉它跳到下一个可以聚焦的元素?


或许是“stopPropagation()”语句的位置有问题?我有点困惑你在代码后面说的话。 - itdoesntwork
1
@itdoesntwork stopPropagation()很有用,因为我们不想冒泡并浪费资源。基本上,当触发blur()时,内部标签索引将被重置,因此每次您使用Tab键时,它都会尝试聚焦在第一个可聚焦元素上,这将导致其失去聚焦,而下一次再次使用Tab键时,则会再次尝试选择它。 - George Reith
e.preventDefault() 有帮助吗? - Eric
@Eric 很遗憾并不能阻止焦点事件。 - George Reith
我必须指出,这种方法可能会使使用屏幕阅读器的人无法使用该页面。试图控制焦点可能会干扰键盘焦点,阻碍人们访问页面的其他部分。 - AlastairC
显示剩余4条评论
5个回答

13
如果您在元素上设置tabindex-1,它将忽略选项卡。
不确定这是否适用于所有浏览器,但它在Google Chrome中有效。
<input type='text' value='regular'/>
<input type='text' tabindex="-1" value='with tabindex set to -1'/>


1
这是一个标准解决方案。https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/tabindex - Reza Nooralizadeh
1
这个回答中的垂直同步更新是不必要的。它使原本简单明了的回答变得复杂化了。 - npn_or_pnp

5

这个有效(已更新):

$body.on("focus.spt", "*", function(e){
  $this = $(this);
  if (!$this.is($mobileMenu) && !$this.is($menuItems)) {
    $this.blur();
    var next=$this.nextAll().find('a,input');
    if (next.length>0) next[0].focus();
  } else {
    console.log('ok',this);
    e.stopPropagation();
  }
})

(更新) fiddle -> http://jsfiddle.net/CADjc/

您可以在控制台中查看哪些元素获得了焦点,包括(main-menu amobile-menu)

已测试:

<input type="text" tabindex="1" value="test">
<span><input type="text" tabindex="2" value="test"></span>
<div><input type="text" id="mobile-menu" tabindex="3" value="mobile-menu"></div>
<div><span>
    <div id="main-menu">
        <a tabindex="4">main-menu</a>
        <a tabindex="5">main-menu</a>
    </div>
</span></div>
<span>
<input type="text" tabindex="6" value="test">
</span>

1
谢谢...但这只适用于元素直接相邻的情况(否则next()无法工作)在流中...请参见http://jsfiddle.net/e96EV/2/ - George Reith
@GeorgeReith,你说得对,我们在自制的测试标记上盲目测试,你本可以提供一个标记示例 :) - 请参见上面的更新以及更新后的代码片段。为什么要关闭您的标记,例如<input ../> ?? (在您的代码片段更新中看到) - davidkonrad
刚才才意识到,它只在允许接收焦点的元素是其父元素中第一个可接收焦点的元素时起作用...请访问http://jsfiddle.net/CADjc/2/ - 我认为这是由于next[0],它只测试第一个找到的元素。此外,我关闭了元素,因为JSFiddle很难突出显示未关闭的元素(即使它们是有效的)。 - George Reith
好的,我已经修复了 next[0] 但它仍然没有环绕。一旦到达末尾,它只是在最后两个之间切换... http://jsfiddle.net/CADjc/3/ - George Reith
更改是必要的,因为如果它在 next[0] 没有获取可聚焦元素,它将只会传播并忽略任何其他可能的兄弟元素。另一个问题是它将首先选择后面的同级元素而不是子元素。 - George Reith
现在,this.blur() 也可以作为方法存在于元素本身上。 - Džuris

5

如果你将某个元素设置为不可用(disabled),它将无法获得焦点。例如:

<input type="text" disabled="disabled" />

要以编程方式添加它,可以这样做:
var el = document.getElementById('disableme');
el.setAttribute('disabled', 'disabled');

2
当表单被提交时,禁用的输入元素不会被发送到服务器。这与当前情况没有太大关系,但记住这一点很重要。 - Antares42
2
我的建议是将单词“something”替换为“表单元素”,因为“disabled”只适用于表单元素,而不适用于其他任何内容。 - Armen Michaeli

2
我过去项目中的CSS-only解决方案。

/* Prevents all mouse interactions */
.disabled-div {
  opacity: 0.5;
  pointer-events: none;
}

/* Prevents all other focus events */
.disabled-div:focus,
.disabled-div:focus-within {
  visibility: hidden;
}
<h1>CSS Only Disable with Prevent Focus</h1>

<input placeholder="Normal text field">
<br><br>
<input class="disabled-div" placeholder="Disabled text field">
<br><br>
<input placeholder="Normal text field">
<br><br>
<input class="disabled-div" placeholder="Disabled text field">
<br><br>

当元素获得焦点时,它会轻微闪烁。这是因为当元素获得焦点时,可见性被设置为'hidden',然后失去焦点,可见性又被设置回'visible'。这实际上是好的,因为用户现在可以大概知道焦点在哪里,同时检查禁用字段...


1
这很棒,但不幸的是在Safari或Firefox上无法运行。 - mhenry1384
我希望这个能够起作用,但在某些浏览器中关闭可见性根本不会影响焦点。我认为你看到的任何相反行为都是非标准的。 - Lummox JR

2

使用attr("readonly","readonly") 可以防止输入框获取焦点和输入值被发送到服务器。


不,readonly 属性并不阻止元素接收焦点,无论是通过按 Tab 键还是点击它。它只是防止编辑元素的值。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接