I modified my Backbone app to render a large table using a DOM document fragment, which results in a nice performance improvement when there are many rows.
Unfortunately, this seems to break event handling, so that the views for each row no longer receive click events. I created a minimal example to demonstrate this.
What do I have to do to fix this?
// Javascript
$(window).load(function(){
var Item = Backbone.Model.extend();
var Items = Backbone.Collection.extend({
model: Item,
count: 5,
initialize: function() {
for(i=0; i<this.count; i++){
this.add(new Item({text: i}));
}
}
});
var ItemView = Backbone.View.extend({
tagName: 'tr',
initialize: function(item) {
this.item = item;
},
render: function() {
this.$el.append('<td class="item">' + this.item.get('text') + '</td>');
return this;
},
events: function() {
return { "click .item": function() {
console.log('handling click on item '+this.item.get('text'));
}
};
}
});
var TableBodyView = Backbone.View.extend({
tagName: 'tbody',
render: function() {
if(this.options.useFragment){
this.renderWithFragment();
} else {
this.renderWithoutFragment();
}
return this;
},
renderWithFragment: function() {
var fragment = document.createDocumentFragment();
this.collection.each(function(item){
fragment.appendChild((new ItemView(item)).render().el);
});
this.$el.append(fragment.cloneNode(true));
},
renderWithoutFragment: function() {
self = this;
this.collection.each(function(item){
self.$el.append((new ItemView(item)).render().el);
});
}
});
var items = new Items();
$('#t1').append((new TableBodyView({collection: items, useFragment: false})).render().el);
$('#t2').append((new TableBodyView({collection: items, useFragment: true})).render().el);
});
// HTML
<table id="t1" />
<table id="t2" />
// CSS
table { float: left; margin: 10px; padding: 5px; }
table#t1 { background: green; }
table#t2 { background: red; }
Your problem is that
cloneNodeexplicitly doesn’t copy event handlers:So, when you do this:
you lose Backbone’s
delegateattached to the view’seland all your event handling is gone. If you want to clone theeland keep the events then you can use thewithDataAndEventsflag with jQuery’sclone:So you should be able to do it like this instead:
Demo (open your console please): http://jsfiddle.net/ambiguous/M6SvH/