From e31c16df43e853e2a29b28ee0b3908684d2f6769 Mon Sep 17 00:00:00 2001 From: David Cook Date: Thu, 20 Feb 2025 12:46:23 +1100 Subject: [PATCH] Delegate events to the parent element Ok so I wasn't as smart as I thought I was. The stimulus controller knows when its element is added/removed from the DOM (and calls connect/disconnect appropriately). But if any child elements are added, they don't automatically have my new event handlers. So I borrowed jQuery's event delegation concept, and listen for any events that 'bubble' up to the controller element, and delegate them as needed. Alternatively, maybe I could have used a Mutation Observer, but I think it's best to avoid where possible. Or of course, we could just revert my change and keep the 'data-action's in the HTML. I'm curious to hear opinions on this.." --- app/webpacker/controllers/checked_controller.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/webpacker/controllers/checked_controller.js b/app/webpacker/controllers/checked_controller.js index fc7ac72b18..e29a4391e6 100644 --- a/app/webpacker/controllers/checked_controller.js +++ b/app/webpacker/controllers/checked_controller.js @@ -7,11 +7,7 @@ export default class extends Controller { connect() { this.toggleCheckbox(); - this.allTarget.addEventListener("change", this.toggleAll.bind(this)); - - this.checkboxTargets.forEach((checkbox) => { - checkbox.addEventListener("change", this.toggleCheckbox.bind(this)); - }); + this.element.addEventListener("change", this.#toggleChangeListener.bind(this), {passive: true}); } toggleAll() { @@ -39,6 +35,15 @@ export default class extends Controller { // private + // Delegate events for targets (this ensures we catch events from newly-added elements after an ajax action) + #toggleChangeListener(event) { + if (event.target == this.allTarget) { + this.toggleAll(); + } else if (this.checkboxTargets.includes(event.target)) { + this.toggleCheckbox(); + } + } + #checkedCount() { return this.checkboxTargets.filter((checkbox) => checkbox.checked).length; }