如何防止单击复选框的标签窃取焦点

Chr*_*alo 8 html javascript

Mac OS X 上的默认行为是单击复选框和按钮不会从其他具有焦点的控件(例如,文本框)窃取焦点。我想在网络上实现这个。

我可以让它适用于按钮和复选框本身,但不适用于复选框标签。

以这个jsbin为例:http ://jsbin.com/kopateluze/1/edit?html,console,output

这是表格的屏幕截图:

http://jsbin.com/kopateluze/1/edit?html,console,output 的截图

如果先聚焦文本框,然后单击按钮,则可以使用event.preventDefault()onmousedown防止文本框失去焦点。这很好用。如果您首先关注文本框,然后单击复选框,则同样有效。

但是如果你先聚焦文本框,然后点击复选框的label文本,就不行了;文本框失去焦点。

有没有办法防止单击复选框label从其他控件窃取焦点?

请使用纯 JavaScript,但请随时链接到库中实现的解决方案的源代码。

Chr*_*alo 11

好吧,这就是我能够弄清楚的事情,两年多一点之后。

防止按钮窃取焦点

首先,让我们谈谈激发这个问题的例子。当文本字段具有焦点时,我想防止单击按钮窃取焦点。我仍然希望调用任何按钮单击处理程序,我只是不想移动焦点。

要做到这一点,我们只需要知道按钮接收焦点mousedown,所以防止它就像使用一样简单event.preventDefault()

<input placeholder="First focus me" type="text" />

<br/>

<button
  onmousedown="event.preventDefault()"
  onclick="console.log('button:click')">
    Then click me
</button>
Run Code Online (Sandbox Code Playgroud)

请注意,单击按钮会触发单击处理程序,但不会使按钮获得焦点。另请注意,按钮仍然可以通过按下 获得焦点Tab,因此这不会损害键盘访问。

防止复选框窃取焦点

事实证明,只要您使用没有标签的裸复选框,同样的技术就可以完美运行。

<input placeholder="First focus me" type="text" />

<br/>

<input
  type="checkbox"
  onmousedown="event.preventDefault()"
  onclick="console.log('checkbox:click')"
/>
? Then click the checkbox
Run Code Online (Sandbox Code Playgroud)

但这确实存在可访问性问题,因为描述复选框的文本与其无关,并且复选框单击目标只是复选框本身,而不是随附的文本。这里的解决方案是添加一个label元素,但这就是问题的开始。

复选框有标签时的问题

<input placeholder="First focus me" type="text">

<br/>

<label>
  <input type="checkbox"
    onmousedown="event.preventDefault()"
    onclick="console.log('checkbox:click')"
  />
  Then click me
</label>
Run Code Online (Sandbox Code Playgroud)

如果您单击复选框,这将成功防止复选框窃取焦点。但是,如果您单击复选框旁边的文本,则会从文本框中窃取焦点。

当复选框有标签时的修复

关键在于了解label元素如何与其关联的input元素相互作用。该label元素的click处理实际上被调用两次:一次是在那里event.targetlabel元素,一旦它在哪里的复选框。

因此,一般的解决方法是:

  1. 调用event.preventDefault()mousedown的标签和复选框的都有。
  2. 在执行两次的标签单击处理程序中,调用.click()关联的控件(复选框元素),但仅当event.target等于该label元素时。

<input placeholder="First focus me" type="text" />

<br/>

<label for="checkbox"
  onmousedown="event.preventDefault()"
  onclick="
    console.log('label:click');
    if (this === event.target) {
      console.log('label === event.target');
      console.log('label:click preventDefault()');
      // prevent focus and click
      event.preventDefault();
      // call click on the labeled control
      this.control && this.control.click();
    }
  ">
  <input id="checkbox" type="checkbox"
    onmousedown="event.preventDefault()"
    onclick="
      console.log('checkbox:click', `checked = ${this.checked}`);
    "
  />
  Then click me
</label>
Run Code Online (Sandbox Code Playgroud)