How to properly remove DOM elements along with specific targets/controllers?

Hi everybody,
in my initialize function I have jQuery code which binds the remove event of the Stimulus myTarget (or its controller) in order to remove a DOM element along with that target (or controller):

initialize() {
  var self = this;

  $(self.myTarget).on({ // or $(self.element).on({ ...
    'remove': function(event) {
      console.log("Remove event run from initialize");
      $('#domElement').remove();
    }
  });
}

Although it works for non-Ajax-loaded content in my page, it doesn’t work for Ajax-loaded content. That is, the message Remove event run from initialize isn’t displayed in console when myTarget (or its controller) is Ajax-added then removed from the DOM, resulting in the #domElement to be not removed.

Instead, the following code works for both Ajax and non-Ajax-loaded content:

disconnect() {
  var self = this;

  if (self.myTarget) {
    console.log("Remove event run from disconnect");
    $('#domElement').remove();
  }
}

That is, the message Remove event run from disconnect is displayed in console in both cases (Ajax and non-Ajax-loaded content), resulting in the #domElement to be removed.

In my application, on the same page, I have multiple Stimulus controllers of the same kind each having its myTarget. The issue with using the disconnect function is that DOM elements are removed indistinctly when targets/controllers are disconnected while I need to remove specific DOM elements along with specific, singular Stimulus targets/controllers. For this reason I thought to bind remove events during initialization for each target/controller.

So, how can I properly bind the remove event to specific targets/controllers during initialization? Should I bind other events than the remove one e.g. someway the Stimulus disconnect event (if possible)?

Or, should I rethink about issue and solution, and for example use global events on data-action or something else to remove one DOM element along with one specific target/controller?

1 Like

The lifecycle events connect and disconnect are invoked when the controller is added and removed from the DOM, but unfortunately there are no such callbacks for targets. Your controller won’t know if targets are added or removed.

You could make a tiny Stimulus controller for your target to tidy up the DOM:

<div data-controller="tidy" data-target="controller.myTarget">
// tidy_controller.js
disconnect() {
  $('#domElement').remove()
}

Thanks for the reply, but the issue still arises by using your solution, and also if I bind the remove event directly to the controller $(self.element) – instead of the target $(self.myTarget) – during initialization.

BTW Question updated by adding the case the controller is bound to the remove event.

I’d love to help, but I think I need a bit more information about your specific use case, and how the elements you want to remove is related to the controller or action. Here are a vague suggestion until then, which might or might not help.

You mention that you need to remove specific elements along with specific actions or controller. Would it in any way be possible to individualize those elements in the DOM? If so, you can use the Stimulus Data API to annotate their selectors on the controller elements and use that information to remove them along with the controller.

<div id="remove-me-one">I'm gonna be removed!</div>
<div id="remove-me-two">I'm gonna be removed!</div>

<div data-controller="outer">
  <div data-controller="inner" data-target="outer.myTarget" data-inner-remove-selector="#remove-me-one"></div>
</div>
<div data-controller="outer">
  <div data-controller="inner" data-target="outer.myTarget" data-inner-remove-selector="#remove-me-two"></div>
</div>
// inner_controller.js

disconnect() {
  $(this.data.get('removeSelector')).remove()
}

Whenever the targets are removed, whether it be with Ajax or other DOM manipulations and whether it’s removed along with the controller or by itself, the annotated element will be removed as well. This also works when the targets are inserted with Ajax.

Let me know if it solves your problem :slight_smile:

Thanks @kaspermeyer, I solved rethinking about issue and solution, and ended up removing elements in the disconnect() function.

BTW remove in initialize() is not a jQuery event…