Memory Leak when deleting items from DOM using backbone
backbone-layout-manager, backbone.js
Solution
There are several problems with your code:
You use `this.$el` as model to trigger the `remove-item` event. You should use your model instead.
The view should wait for events from the model to know when to remove itself.
Here's the code I come up with. If it doesn't work, post your code somewhere so I can run it myself.
var ViewToDelete = Backbone.View.extend({
template: "ViewToDelete",
tagName: "li",
events: {
"click .delete-button": "deleteItem"
},
initialize: function () {
this.listenTo(this.model, 'remove', this.remove);
},
deleteItem: function() {
this.model.remove();
}
});
The default implementation of `view.remove()` will remove `this.$el` and stop listening to the model:
remove: function() {
this.$el.remove();
this.stopListening();
return this;
},
EDIT: Thank you for posting your code online. Here's what I think is happening (I'm also documenting for future viewers).
If you take a snapshot, filter on Detached DOM Tree, you see:
The important part is the retaining tree: references that prevent the LI from being deleted. The only significant thing is `sizzle-1364380997635`. It doesn't come from your code, it actually comes from jQuery, more specifically from its Sizzle engine. The key comes from here:
https://github.com/jquery/sizzle/blob/master/sizzle.js#L33
If you look further in the code, you see that there's a cache:
https://github.com/jquery/sizzle/blob/master/sizzle.js#L1802
So, in a nutshell, you code does not leak, but jQuery has an internal cache that prevents it from being removed anyway. This cache can only contain a few dozen elements, so it won't retain elements forever.
Problem
I am having issues with DOM elements being left in memory after being deleted. I have set-up an example shown below. Note I am using the backbone layout manager plugin to manage my views (as well as jQuery). I have done a heap snapshot in Chrome before and after deleting one of the items in the list and compared the two: As you can see the LI element is still in memory. Backbone Layout Manager does call view.unbind() and view.stopListening() when a view is removed. Below is the example code. ListOfViewsToDelete.js ``` var TestModel = Backbone.Model.extend({ }); var TestCollection = Backbone.Collection.extend({ model: TestModel, }); var ViewToDelete = Backbone.View.extend({ template: "ViewToDelete", tagName: "li", events: { "click .delete-button": "deleteItem" }, deleteItem: function() { this.$el.trigger('remove-item', [this.model.id]); } }); var ListOfViewsToDelete = Backbone.View.extend({ template: "ListOfViewsToDelete", initialize: function() { this.collection = new TestCollection(); for (var i = 0; i < 5; i++) { this.collection.add(new TestModel({id: i})); } this.listenTo(this.collection, 'all', this.render); }, events: { "remove-item": "removeItemFromCollection" }, beforeRender: function() { this.collection.each(function(testModel) { this.insertView("ul", new ViewToDelete({ model: testModel })); }, this); }, removeItemFromCollection: function(event, model) { this.collection.remove(model); } }); ``` router.js ``` app.useLayout("MainLayout").setViews({ "#main": new ListOfViewsToDelete() }).render(); ``` ListOfViewsToDelete.html ``` <ul> </ul> ``` ViewToDelete.html ``` View to delete <button class="delete-button">x</button> ```