我有一个简单的骨干视图如下:
/**
* Renders a form view for an event object.
*/
APP.EventFormView = Backbone.View.extend({
tagName: 'form',
events: {
'keydown': 'keyPressed',
'focus input': 'inputChanged',
'change select': 'selectChanged',
'change textarea': 'textareaChanged'
},
initialize: function() {
this.template = _.template($('#newevent-form').html());
this.listenTo(this.model, 'change', this.render);
this.listenTo(APP.eventTypes, 'update', this.render);
this.listenTo(APP.selectedEvent, 'update', this.render);
},
render: function() {
var modelJSON = this.model.toJSON();
if ('id' in modelJSON && modelJSON.id !== "") {
this.loadForm();
} else if (!('id' in modelJSON) || modelJSON.id === "") {
this.loadForm();
} else {
this.$el.html('');
}
return this;
},
loadForm: function() {
var templateData = $.extend(this.model.toJSON(),
{"event_types":APP.eventTypes.toJSON()});
this.$el.html('');
this.$el.html(this.template($.extend(this.model.toJSON(),
{event_types: APP.eventTypes.toJSON()})));
$('.ev-main-container').html('').html(this.el);
},
inputChanged: function(e) {
console.log('inputChanged');
},
selectChanged: function(e) {
console.log('selectChanged');
},
textareaChanged: function(e) {
console.log('textareaChanged');
},
keyPressed: function(e) {
console.log('key pressed');
}
});
我在document.ready:
下初始化此视图如下// Initialize the form view
APP.selectedEvent = APP.selectedEvent || new APP.Event();
APP.eventFormView = new APP.EventFormView({model: APP.selectedEvent});
APP.eventFormView.render();
但是我所定义的事件都没有因为某些原因被解雇,我在这里做错了什么?
好的,如果我从$('.ev-main-container').html('').html(this.el);
方法中删除loadForm
并将该视图初始化,如下所示,那么我就会发挥作用:
APP.eventFormView = new APP.EventFormView({
model: APP.selectedEvent,
el: $('.ev-main-container'),
});
我能够解决它,但我仍然不明白为什么会发生这种情况,任何人都可以对正在发生的事情及其运作方式稍作了解。
答案 0 :(得分:2)
jQuery的html
函数有许多人似乎忘记的副作用,来自fine manual:
在使用新内容替换这些元素之前,jQuery会从子元素中删除其他构造(如数据和事件处理程序)。
当你做这样的事情时,请考虑一下这意味着什么:
container.html(view.el);
container.html(view.el);
第一次container.html()
电话后,一切都会好起来的。但第二个将"从子元素中删除...事件处理程序" (例如view.el
)在添加新内容之前。因此,在第二次container.html()
调用之后,view.el
上的所有事件都消失了。听起来很熟悉吗?
您有许多事情会在您的观看中调用render
,render
最终会这样做:
$('.ev-main-container').html('').html(this.el);
第二次调用时,你的事件会默默地消失,但HTML看起来会很好。
考虑这个简化的例子(http://jsfiddle.net/ambiguous/otnyv93e/):
var V = Backbone.View.extend({
tagName: 'form',
events: {
'click button': 'clicked'
},
initialize: function() {
this.template = _.template($('#t').html());
},
render: function() {
this.$el.html('');
this.$el.html(this.template());
$('.ev-main-container').html('').html(this.el);
return this;
},
clicked: function() {
console.log('clicked');
}
});
var v = new V;
v.render();
$('#re-render').click(function() {
v.render();
console.log('Re-rendered');
});
你会看到你的问题。
如果您将el
.ev-main-container
视为html()
,那么您将使用el
更改el
的内容,而不是更改el
包含 el
的元素的内容。一旦您完全在el
内部工作,您就不会再意外地重复使用元素,并且不再意外地从该元素中删除事件绑定。
我的经验法则是防止Backbone出现事件:
remove
,并让调用者将remove
放入容器中。el
,以便在不再需要时对其进行处理。void OnPrimaryClipChanged (String text) {
MyLocalClip.save(text);
ClipboardManager.setText("");
}
视图。onPause
以外的任何内容。也有例外(当然),这种方法不会解决所有问题,但这是一个很好的起点,避免了大多数常见问题。