Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way to unbind event listeners and remove child elements in single page applications?

I've been building a large single page application and recently got into exploring memory leaks in JS. And I think I've got a memory leak because - as I use the Profiles (Snapshot) function in Chrome - I see that I have a lot of detached DOM elements.

Here is a simplified view of my setup:

<div id="container">
  <div class="buttons">
    <a class=".btn" href="/someurl/"> Button A</a>
    <a class=".btn" href="/someurl/"> Button B</a>
    <a class=".btn" href="/someurl/"> Button C</a>
  </div>

  <div class="ajaxHolder"></div>
</div>

So if a user clicks Button A, for example, I would use an AJAX call to load content into the .ajaxHolder. Something like this:

//This is the content...
<div class="contentA">
  <p>some text...</p>
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
</div>

I also have two functions inside my MAIN script file. One would be like this:

//Click event bound to a.btn which tigger the ajax call
$(.buttons).on('click', '.btn', function(){ 
  //Do ajax here...
  //on success...
  var holder = $(".ajaxHolder");
  holder.children().off().remove(); // Is this the way to do this??
  holder.off().empty(); //I don't think I need .off() here, but I guess it can't hurt...
  holder.append(ajaxSuccessData);
});

So far so good. But now with the content loaded, I also have this function inside my MAIN script file:

//So here, I am binding an event to the checkboxes...
$(".ajaxHolder").on('change', '.checkbox', function(){
  //Do something...
});

So now if the user presses button B, for example, .ajaxHolder is emptied, but all those events tied to my checkboxes seem to stick around, because they are showing up in my detached DOM tree.

What am I missing here? How can I make sure I don't have any detached DOM elements, in a single page application running like this?

BONUS QUESTION: I have A LOT of events tied like this:

$(".ajaxHolder").on('someEvent...','.someClass....', someFunction());

That is to say, everything is always attached to my .ajaxHolder, because I have to use event delegation since .ajaxHolder will always exist - whereas other items that are loaded will not always exist, so I cannot simply do $(button).on('click')..., etc.

So is this the correct way to do this as well? Is there a limit to how many things I can attach to this .ajaxHolder or are there no issues with doing this?

EDIT: Here is an image of my console, if that helps at all.

enter image description here

like image 682
Amir Avatar asked Feb 17 '16 20:02

Amir


1 Answers

Not sure this is still relevant, but from the console image I gather you are using bootstrap tooltips, and they seem to be holding the reference to the DOM node. I suggest you call the destroy method via the API

like image 102
thedude Avatar answered Oct 17 '22 13:10

thedude