Backbone 查看事件未触发
Backbone view events do not fire
我有一个简单的backbone视图如下:
/**
* 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();
但是我定义的 none 个事件由于某种原因正在触发,我在这里做错了什么?
更新:
好吧,如果我从 loadForm
方法中删除 $('.ev-main-container').html('').html(this.el);
而不是按如下方式初始化视图,我就搞砸了,它有效:
APP.eventFormView = new APP.EventFormView({
model: APP.selectedEvent,
el: $('.ev-main-container'),
});
我能够解决它,但我仍然不明白为什么会这样,任何人都可以稍微了解一下发生了什么以及它是如何工作的。
jQuery 的 html
函数有一个副作用,很多人似乎忘记了,来自 fine manual:
jQuery removes other constructs such as data and event handlers from child elements before replacing those elements with the new content.
想想当你做这样的事情时这意味着什么:
container.html(view.el);
container.html(view.el);
第一次 container.html()
调用后一切都会好起来的。但是第二个会"remove ... event handlers from child elements"(比如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
中工作,您就不会再不小心重复使用某个元素,也不会再不小心从该元素中删除事件绑定。
我防止 Backbone 事件问题的经验法则:
- 永远不要将视图附加到现有的 DOM 节点,始终让视图创建并拥有自己的
el
并让调用者将 el
放入容器中。
- 调用
remove
视图以在不再需要时处理它们。
- 不要尝试重复使用视图,在需要时创建它们,在不需要时
remove
它们。
- 没有视图引用其
el
之外的任何内容。
有例外(当然),这种方法不能解决所有问题,但它是一个很好的起点,可以避免大多数常见问题。
我有一个简单的backbone视图如下:
/**
* 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();
但是我定义的 none 个事件由于某种原因正在触发,我在这里做错了什么?
更新:
好吧,如果我从 loadForm
方法中删除 $('.ev-main-container').html('').html(this.el);
而不是按如下方式初始化视图,我就搞砸了,它有效:
APP.eventFormView = new APP.EventFormView({
model: APP.selectedEvent,
el: $('.ev-main-container'),
});
我能够解决它,但我仍然不明白为什么会这样,任何人都可以稍微了解一下发生了什么以及它是如何工作的。
jQuery 的 html
函数有一个副作用,很多人似乎忘记了,来自 fine manual:
jQuery removes other constructs such as data and event handlers from child elements before replacing those elements with the new content.
想想当你做这样的事情时这意味着什么:
container.html(view.el);
container.html(view.el);
第一次 container.html()
调用后一切都会好起来的。但是第二个会"remove ... event handlers from child elements"(比如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
中工作,您就不会再不小心重复使用某个元素,也不会再不小心从该元素中删除事件绑定。
我防止 Backbone 事件问题的经验法则:
- 永远不要将视图附加到现有的 DOM 节点,始终让视图创建并拥有自己的
el
并让调用者将el
放入容器中。 - 调用
remove
视图以在不再需要时处理它们。 - 不要尝试重复使用视图,在需要时创建它们,在不需要时
remove
它们。 - 没有视图引用其
el
之外的任何内容。
有例外(当然),这种方法不能解决所有问题,但它是一个很好的起点,可以避免大多数常见问题。