The default behavior on Mac OS X is that clicking checkboxes and buttons doesn't steal focus from other controls that have focus (for example, a textbox). I want to implement this on the web.
I can get it working for buttons and checkboxes themselves but not for checkbox labels.
Take this jsbin as an example:
http://jsbin.com/kopateluze/1/edit?html,console,output
Here is a screenshot of the form:
If you first focus the textbox and then click the button, you can use event.preventDefault()
on mousedown
to prevent the textbox from losing focus. That works nicely. And if you first focus the textbox and then click the checkbox, the same thing works.
But if you first focus the textbox and then click the checkbox's label
text, it doesn't work; the textbox loses focus.
Is there a way to prevent clicking on a checkbox label
from stealing focus from other controls?
Pure JavaScript, please, but feel free to link to the source code for solutions implemented in libraries.
Okay, here's what I was able to figure out, a little over two years after the fact.
First, let's talk about the example that motivated this question. When a text field has focus, I'd like to prevent clicking buttons from stealing focus. I still want any button click handlers to be called, I just don't want focus to move.
To do that, we just need to know that buttons receive focus on mousedown
, so preventing that is as simple as using event.preventDefault()
:
<input placeholder="First focus me" type="text" />
<br/>
<button
onmousedown="event.preventDefault()"
onclick="console.log('button:click')">
Then click me
</button>
Notice that clicking the button triggers click handlers but never causes the button to receive focus. Also note that buttons can still receive focus via pressing Tab
, so this doesn't harm keyboard access.
It turns out, as long as you use a naked checkbox that has no label, this same technique works perfectly.
<input placeholder="First focus me" type="text" />
<br/>
<input
type="checkbox"
onmousedown="event.preventDefault()"
onclick="console.log('checkbox:click')"
/>
⇐ Then click the checkbox
But this does have accessibility problems because the text describing the checkbox isn't associated with it and the checkbox click target is just the checkbox itself, not the text that goes with it. The solution here is to add a label
element, but this is where the problems start.
<input placeholder="First focus me" type="text">
<br/>
<label>
<input type="checkbox"
onmousedown="event.preventDefault()"
onclick="console.log('checkbox:click')"
/>
Then click me
</label>
This successfully prevents the checkbox from stealing focus if you click on the checkbox. However, if you click on the text next to the checkbox, focus is stolen from the textbox.
The key is in knowing how label
elements interact with their associated input
elements. The label
element's click
handler actually gets called twice: once where event.target
is the label
element, and once where it is the checkbox.
Therefore, the general fix is to:
event.preventDefault()
on mousedown
of both the label and the checkbox..click()
on the associated control (the checkbox element), but only when the event.target
is equal to the label
element.<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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With