-
Notifications
You must be signed in to change notification settings - Fork 426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Event not bubbling to parent controller #363
Comments
I had some other issues with nested controllers couldn't really find a solution. Not sure if they are related. Hopefully someone can propose a nice solution for both |
Interestingly I have the reverse experience with a stimulus 1.1.1 controller, whereby an event bubbles even though I ask for it not to do so: export default class extends Controller {
remove(event) {
console.log("remove")
event.cancelBubble = true
// this.element.remove() - // This causes the event to bubble
setTimeout(() => {
this.element.remove() // This setTimeout tricks makes it not bubble
}, 0)
}
} Clicking the inner button deletes the outer group, but displays 'remove' twice in the console. <div data-controller="group">
<button data-action="rules-group#remove"></button>
<div data-controller="group">
<button data-action="rules-group#remove"></button>
</div>
</div> |
Hey Ryan, thanks for opening the issue! This is intentional behavior, but I admit we've done a poor job communicating it in the documentation. The main concept to understand is scopes. A scope is the set of elements known to a controller. Specifically, the controller's scope says which elements are eligible to be targets and which elements can connect actions. In general, a controller's scope is the controller element plus all its children: As you've discovered, Stimulus treats nested scopes differently. A scope's elements are exclusive with respect to the controller identifier. When we add a new controller with the same identifier as another controller on a parent element, we create a new scope, and the outer scope no longer contains its elements: The final piece to note: An action connects to a controller through a binding, which is responsible for receiving events and invoking the corresponding action method. Bindings filter out events whose target element is not part of the controller's scope. I would be curious to hear more about how your gallery controller is set up. I suspect the best solution is to use two separate controllers. |
Thanks for the detailed response. Scoping for the most part works intuitively. I like that targets and actions only apply to the closest controller. My biggest issue is with this:
I think events should follow the normal JS bubble behavior. If we are listening to that event on a DOM element, that element becomes the Here's an example of why I find it unintuitive: <div data-controller="alt-gallery" data-action="click->alt-gallery#next">
<div data-controller="gallery" data-action="click->gallery#next">
<div data-controller="gallery">
<button>Click me!</button>
</div>
</div>
</div> The Here's my use case. I have a nested set of lists with a stimulus controller on each item to expand the nested list (think folders in a file system list view). I have another button on each item to expand the deepest part of the tree which is collapsed. The tricky part is I want to visually disable that button when there are no further descendants to expand. This means the parent button needs to update when a descendant expands/collapses. I attempted to do this by emitting a custom event whenever expanding/collapsing the list. I expected the event to bubble up to each ancestor controller so I can update the button which manages the deep expansion. My work around is re-triggering the event on the parent element so it can escape the scope. The downside is we lose context of the target, but that can be passed through event
This may be difficult to do since the nested lists can be arbitrarily deep. I could have a separate controller which wraps everything to manage updating the buttons, however I think this will be more complex than my current work around since the logic of what needs to be updated nicely follows the DOM tree and you'd lose that context. I would be interested to hear counter examples where the current behavior is desirable and intuitive. |
I've encountered this issue as well, and agree with @ryanb. In my case, I'm using a "reveal" controller in a nested list. One use of this controller is to open a list item to reveal its children. Another use is during sorting, where I display modal for saving changes when sorting is changed. So, I have the following: <div data-controller="reveal">
<div data-action="sort->show#reveal">
<!-- top-level list -->
<ul data-controller="sortable">
<li>
<!-- ... item content ... -->
<div data-controller="reveal">
<!-- nested list -->
<ul data-controller="sortable">
<li>
<!-- ... item content ... -->
</li>
</ul>
</div>
</li>
</ul>
<div>
<div data-reveal-target="item">
<!-- modal for saving changes -->
</div>
</div> When my sorting library triggers the The explanation @sstephenson provided does a great job at clarifying why |
If I have two of the same controllers nested, the action does not bubble up to the parent controller.
Here the
gallery#next
action is not triggered even though the event bubbles up to this DOM element.Here is a CodePen example.
There you can see that if the parent controller is different from the child controller it triggers the action correctly. I expect nesting the same controllers to behave the same way.
This is using Stimulus 2.0.0.
The text was updated successfully, but these errors were encountered: