question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Event not bubbling to parent controller

See original GitHub issue

If I have two of the same controllers nested, the action does not bubble up to the parent controller.

<div data-controller="gallery" data-action="click->gallery#next">
  <div data-controller="gallery">
    <button>Click me!</button>
  </div>
</div>

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.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

8reactions
ryanbcommented, Jan 12, 2021

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:

Bindings filter out events whose target element is not part of the controller’s scope.

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 currentTarget as the event bubbles up. At that point it feels like that event has escaped the inner scope. One can call event.stopPropagation() if they don’t want it to bubble up to a parent controller.

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 gallery#next is not triggered but alt-gallery#next is triggered. The event basically jumps over the middle controller.


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 detail if needed.

I suspect the best solution is to use two separate controllers.

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.

4reactions
jankocommented, May 27, 2021

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 sort event on the top-level ul, show#reveal is correctly called. But when the sort event is triggered on the ul inside the nested reveal Stimulus controller, the top-level show#reveal is not being called.

The explanation @sstephenson provided does a great job at clarifying why show#reveal wasn’t called in this example. Are there cases where this behaviour is desired? It doesn’t feel right that a child controller takes over the parent controller just because they have the same name. If I remove the inner reveal controllers, then it works, so the fact that there is a sortable controller nested doesn’t seem to trigger this issue (presumably because it’s a controller with a different name).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Event not Bubbling to Parent Element from Child Element
Your HTML is invalid. An <article> can't be a child of a <p> - check out the rendered HTML: console.log(document.body.innerHTML);
Read more >
Event bubbling, capturing and how to prevent them
So when a event is emitted on the child it will bubble up to its parent. And it makes sense if you think...
Read more >
How to Handle Click Events Not Bubbling Up to Document
The most obvious solution is to stopPropagation of the dropdown clicks to prevent the bubble-up. ... This fixes the unwanted expand/collapse of the...
Read more >
Bubbling and capturing - The Modern JavaScript Tutorial
On a parent element (meaning an element which is not the target), listeners in the capturing phase will trigger before those for the...
Read more >
Configure Event Propagation - Salesforce Developers
Events bubble up through the DOM; that's how children and parents communicate—props down, events up. When an event bubbles, it becomes part of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found