For some reason, my stimulus controller, load once, but the action is fired twice on click.
I can't find where the issue is...
My code is pretty simple though, so I will share it below:
// app/javascript/application.js
https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
// app/javascript/controllers/application.js
import { Application } from "@hotwired/stimulus"
const application = Application.start()
application.debug = false
window.Stimulus = application
export { application }
// app/javascript/controllers/index.js
import { application } from "controllers/application"
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
// app/javascript/controllers/radio_button_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static classes = [ 'active', 'inactive', 'invisible' ]
connect() {
console.log('connected to ', this.element.querySelector('input').value)
}
markAsChecked(event) {
event.stopPropagation();
console.log('-', this.element.querySelector('input').value);
}
}
// view.html.erb
<div class="flex flex-1 gap-x-8 justify-start">
<label data-controller="radio-button" data-action="click->radio-button#markAsChecked:stop">
<input type="radio" value="public" name="destination[access_type]" id="destination_access_type_public">
</label>
<label data-controller="radio-button" data-action="click->radio-button#markAsChecked:stop">
<input type="radio" value="private" name="destination[access_type]" id="destination_access_type_private">
</label>
<label data-controller="radio-button" data-action="click->radio-button#markAsChecked:stop">
<input type="radio" value="backend" name="destination[access_type]" id="destination_access_type_backend">
</label>
</div>
When I run the following commands in the developer console, I also get double action firing
// developer console
temp1 // label
temp1.click()
// - public
// - public
temp2 // input
temp2.click()
// - public
As you can see, markAsChecked is triggered twice when I click on the label, and once when I click on the input. I have no idea why...
(I expect it to always trigger once)
It's the default <label>
element behavior:
When a user clicks or touches/taps a label, the browser passes the focus to its associated input (the resulting event is also raised for the input)...
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
...clicking the
label
in the following snippet could trigger the user agent to fire aclick
event at theinput
element, as if the element itself had been triggered by the user:<label><input type=checkbox name=lost> Lost</label>
https://html.spec.whatwg.org/multipage/forms.html#the-label-element
Note that when clicking a <label>
you get two events but event.target
is different:
document.addEventListener("click", (event) => {
console.log(event.target);
})
<label for="destination_access_type_public">click me</label>
<input type="radio" value="public" name="destination[access_type]" id="destination_access_type_public">
You can add data-action
to input
to always get a single event:
<label data-controller="radio-button">
<input data-action="click->radio-button#markAsChecked" type="radio" value="public" name="destination[access_type]" id="destination_access_type_public">
</label>