In the following example, when you click on the label, the input changes state.
document.querySelector("label").addEventListener("click", function() {
console.log("clicked label");
});
label {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
<input type="checkbox" id="1">
<label for="1">Label</label>
In Chrome, when you move the cursor between the mousedown
and mouseup
events the input still gets triggered, whereas in Firefox the checkbox doesn't change state.
Is there a way to fix this? (without using JavaScript event listeners)
Firefox version: 69.0.3 (64-bit)
Full set of actions when using chrome.
Labels should have “for” attributes that match the ID of the input they are labeling. This means we can snag that attribute and use it in a selector to trigger a click on the input itself. Assuming of course you have some reason to watch for clicks on inputs.
If we have some standard HTML where the label comes before the input: That places the label before the input in the DOM. But now let’s say our label and form are inside a flexible container and we use CSS order to reverse things where the input visually comes before the label:
The form control that the label is labeling is called the labeled control of the label element. One input can be associated with multiple labels. When a <label> is clicked or tapped and it is associated with a form control, the resulting click event is also raised for the associated control.
A screen reader user, who is navigating between elements, might expect the input to gain focus before the label because the input comes first visually. But what really happens is the label comes into focus instead. See here for the difference between navigating with a screen reader and a keyboard. So, we should be mindful of that.
No. This looks like a firefox bug and not an issue with your code. I don't believe there is a css workaround for this behavior.
You may be able to report it to Mozilla and get the issue fixed, but I wouldn't rely on that. https://bugzilla.mozilla.org/home
For a potential workaround I would suggested triggering the event on mouseup instead.
Although I specifically stated in the question that the answer shouldn't involve JavaScript, all the answers worked with JavaScript.
Since this seems to be a Firefox bug and most of the answers submitted at this point would require me to also alter the rest of my code, I decided to create a script that can be run once, will deal with all the labels regardless of when they are added to the dom and will have the least impact on my other scripts.
var mutationConfiguration = {
attributes: true,
childList: true
};
if (document.readyState === "complete") onLoad();
else addEventListener("load", onLoad);
var managingDoms = [];
function onLoad() {
document.querySelectorAll("label[for]").forEach(manageLabel);
if (typeof MutationObserver === "function") {
var observer = new MutationObserver(function(list) {
list.forEach(function(item) {
({
"attributes": function() {
if (!(item.target instanceof HTMLLabelElement)) return;
if (item.attributeName === "for") manageLabel(item.target);
},
"childList": function() {
item.addedNodes.forEach(function(newNode) {
if (!(newNode instanceof HTMLLabelElement)) return;
if (newNode.hasAttribute("for")) manageLabel(newNode);
});
}
}[item.type])();
});
});
observer.observe(document.body, mutationConfiguration);
}
}
function manageLabel(label) {
if (managingDoms.includes(label)) return;
label.addEventListener("click", onLabelClick);
managingDoms.push(label);
}
function onLabelClick(event) {
if (event.defaultPrevented) return;
var id = this.getAttribute("for");
var target = document.getElementById(id);
if (target !== null) {
this.removeAttribute("for");
var self = this;
target.click();
target.focus();
setTimeout(function() {
self.setAttribute("for", id);
}, 0);
}
}
label {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
padding: 10px;
border: 1px solid black;
cursor: pointer;
}
<input type="checkbox" id="a">
<input type="text" id="b">
<label for="a">A</label>
<script>
setTimeout(function() {
var label = document.createElement("label");
label.setAttribute("for", "b");
label.textContent = "b";
document.body.appendChild(label);
}, 3E3);
</script>
onLabelClick
The function onLabelClick
needs to get called whenever a label gets clicked, it will check if the label has a corresponding input element. If it does, it will trigger it, remove the for
attribute of the label so that the browsers don't have the bug won't re-trigger it and then use a setTimeout
of 0ms
to add the for
attribute back once the event has bubbled up. This means event.preventDefault
doesn't have to get called and thus no other actions/events will get cancelled. Also if I need to override this function I just have to add an event-listener that calls Event#preventDefault
or removes the for
attribute.manageLabel
The function manageLabel
accepts a label checks if it has already been added an event listener to avoid re-adding it, adds the listener if it hasn't already been added, and adds it to the list of labels have been managed.onLoad
The function onLoad
needs to get called when the page gets loaded so that the function manageLabel
can be called for all the labels on the DOM at that moment. The function also uses a MutationObserver to catch any labels that get added, after the load has been fired (and the script has been run).
The code displayed above was optimized by Martin Barker.
I know you did not want JS Event listeners, but im thinking you mean to identify the movement this does not but is using mousedown instead of click (mousedown followed by mouseup).
While this is a known bug in Firefox you could get around it by using the mousedown event
I have had to change your id to be a valid one id's must start with a character
document.querySelector("label").addEventListener("mousedown", function(evt) {
console.log("clicked label");
// if you want to to check the checkbox when it happens,
let elmId = evt.target.getAttribute("for")
let oldState = document.querySelector("#"+elmId).checked;
setTimeout(() => {
if(oldState == document.querySelector("#"+elmId).checked){
document.querySelector("#"+elmId).checked = !oldState;
}
}, 150)
});
label {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
<input type="checkbox" id="valid_1">
<label for="valid_1">Label</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