We all know doing something like this is bad:
<ul>
<li>Item</li>
<li>Item</li>
... 500 more list items
</ul>
and then...
$("ul li").bind("click", function() { ... });
I've been looking through a lot of Backbone examples / guides and the following seems to be a standard approach to rendering a list with items, based from a collection of models.
var ListView = Backbone.View.extend() {
tagName: 'ul',
render: function() {
this.collection.each(function(item) {
var view = new ListItemView({model: item});
$(this.el).append(view.render().el);
});
return this;
}
});
A list item view:
var ListItemView = Backbone.View.extend() {
tagName: 'li',
events: {
'click' : 'log'
}
log : function() {
console.log(this.model.get("title"));
}
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
If I'm not mistaken, instantiating the listView with a collection with 500 models, gives me 500 click events, one for each row. This is bad right?
I know Backbone has built in event delegation for namespaced events:
events : {
'click li' : 'log'
}
I suppose I could put this in my ListView, and it would only create one click event for the entire list, but then I wouldn't be able access to model data corresponding to the clicked list item.
What patterns do backbone developers use to solve this typical problem?
We all know doing something like this is bad:
<ul>
<li>Item</li>
<li>Item</li>
... 500 more list items
</ul>
and then...
$("ul li").bind("click", function() { ... });
I've been looking through a lot of Backbone examples / guides and the following seems to be a standard approach to rendering a list with items, based from a collection of models.
var ListView = Backbone.View.extend() {
tagName: 'ul',
render: function() {
this.collection.each(function(item) {
var view = new ListItemView({model: item});
$(this.el).append(view.render().el);
});
return this;
}
});
A list item view:
var ListItemView = Backbone.View.extend() {
tagName: 'li',
events: {
'click' : 'log'
}
log : function() {
console.log(this.model.get("title"));
}
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
If I'm not mistaken, instantiating the listView with a collection with 500 models, gives me 500 click events, one for each row. This is bad right?
I know Backbone has built in event delegation for namespaced events:
events : {
'click li' : 'log'
}
I suppose I could put this in my ListView, and it would only create one click event for the entire list, but then I wouldn't be able access to model data corresponding to the clicked list item.
What patterns do backbone developers use to solve this typical problem?
Share Improve this question edited Dec 22, 2011 at 12:18 Daniel asked Dec 22, 2011 at 12:11 DanielDaniel 5,0244 gold badges35 silver badges34 bronze badges3 Answers
Reset to default 9Derick Bailey wrote a detailed blog post about this dilemma, you can check it out here: http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
You can associate the instance with an element like so:
events : {
'click li' : 'log'
},
log: function( e ) {
var elm = e.currentTarget //Same as `this` in normally bound jQuery event
jQuery.data( elm, "viewInstance" ).log( e );
},
Then:
var ListItemView = Backbone.View.extend() {
tagName: 'li',
log : function() {
console.log(this.model.get("title");
}
render: function() {
//Associate the element with the instance
$(this.el).html(this.template(this.model.toJSON())).data( "viewInstance", this );
return this;
}
});
Keep track of the subviews from the parent view. Then when adding the subview add it to the hash as well as add the cid to the el of the subview. This way have a pointer to the subview and could perform operations on its model etc...
I have not tested this exact code below so THIS may be wrong in a place or two but I have tested this general principle. I have also omitted the listitemview code.
var ListView = Backbone.View.extend() {
subViews: {},
tagName: 'ul',
events: {
'click li' : 'clickItem'
},
clickItem: function(event){
var id = event.currentTarget.cid;
var subView = this.subViews[id];
},
render: function() {
this.collection.each(function(item) {
var view = new ListItemView({model: item});
this.subViews[view.cid] = view;
subEl = view.render().el;
subEl.cid = view.cid;
$(this.el).append(subEl);
});
return this;
}
});