How to get the current element triggered?

I would like to get the current element which has been triggered by an event.

I’ve got a loop of data-target="UsersPageAccountNewsletters.checkbox" elements.

And I’ve got this controller:

import { Controller } from "stimulus"

class UsersPageAccountNewsletters extends Controller {
  static targets = ["checkboxContainer", "checkbox"]
  }

  handleChangeCheckbox(ev) {
    console.log(this.checkboxTarget.checked)
    console.log(ev.target.checked)
  }
}

export default UsersPageAccountNewsletters

However, when I change my checkbox, I can see that this.checkboxTarget.checked != ev.target.checked. Indeed, this.checkboxTarget will be the first element of my checkboxes where ev will be the current element changed.

As I can understand, doing this.checkboxTarget is like doing document.querySelector(checkbox), which gives you the first node of the node list.

In my opinion, it’s a bit sad that this.checkboxTarget doesn’t mean here event as I did this:

data-target="UsersPageAccountNewsletters.checkbox"
data-action="change->UsersPageAccountNewsletters#handleChangeCheckbox"

What do you think? Did I miss something in the API of stimulus?

Hey there! You’re right that this.checkboxTarget always returns the first matching target in the controller’s scope.

I’d suggest reviewing the Event Objects section of the Stimulus Reference. You’ll want each checkbox to look like this:

<input type="checkbox"
  data-target="users-page-account-newsletters.checkbox"
  data-action="users-page-account-newsletters#toggleSubscription">

Then, inside your toggleSubscription action, event.currentTarget will give you the checkbox that changed. (That works even if you click a <label> element linked to the input.)

Note a couple of other things in the snippet above:

4 Likes

Also note that you probably don’t need the data-target attribute unless you plan on doing something with all the checkboxes in aggregate. A good example of where they’d be useful is for implementing “subscribe to all” and “unsubscribe from all” actions:

class extends Controller {
  subscribeToAll() {
    this.checkboxTargets
      .findAll(target => !target.checked)
      .forEach(target => target.click())
  }

  unsubscribeFromAll() {
    this.checkboxTargets
      .findAll(target => target.checked)
      .forEach(target => target.click())
  }
}
2 Likes

Super interesting! Thank you for your time and your clear explanation @sam.