我已经在主干视图上创建了两次警报触发的快速示例。
http://jsfiddle.net/feronovak/8svqX/
这没什么特别的,我真的应该理解如何正确调用单独的视图,所以它们不会触发先前的click方法。现在我生成两个视图,第二个视图覆盖#boxSearch中的表单。当我点击搜索按钮时,它也会生成警报doSearch SearchLocation
。我期待只看到警告doSearch SearchLatitude
。我做错了什么?
var Geo = {
Views: {},
Templates: {}
};
Geo.Templates.SearchLocation = _.template( $("#tpl-search-location").html());
Geo.Templates.SearchLatitude = _.template( $("#tpl-search-latitude").html());
Geo.Views.SearchLocation = Backbone.View.extend({
initialize: function() {
this.render();
},
el: $("#boxSearch"),
template: Geo.Templates.SearchLocation,
render: function()
{
$(this.el).html(this.template);
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(e) {
e.preventDefault();
alert('doSearch SearchLocation');
}
});
Geo.Views.SearchLatitude = Backbone.View.extend({
initialize: function() {
this.render();
},
el: $("#boxSearch"),
template: Geo.Templates.SearchLatitude,
render: function()
{
$(this.el).html(this.template);
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(e) {
e.preventDefault();
alert('doSearch SearchLatitude');
}
});
$(document).ready(function(e) {
var searchLocation = new Geo.Views.SearchLocation();
var searchLatitude = new Geo.Views.SearchLatitude();
});
答案 0 :(得分:19)
这是Backbone应用程序经常出现的“鬼视图”问题:
正如@machineghost指出的那样,您的代码中没有任何内容可以取消绑定第一个视图,因此这两个视图都会附加到#boxSearch
,并且这两个视图都会响应click
事件。有几种方法可以解决这个问题:
您可以在事件处理程序中调用e.stopPropagation()
(docs)(我认为这是您尝试使用e.preventDefault()
进行的操作)。这将防止事件在触发后冒泡,因此最后定义的视图将捕获它。这有效,但请注意,您仍然可以看到两个视图,过时的鬼视图可能会产生其他副作用。
您可以让每个视图使用类.boxSearch
呈现自己的DOM元素,然后在您不再需要的那个上调用view.remove()
。这可能是最干净的选择,但它意味着动态创建大多数DOM,这比用HTML编码更昂贵,更难管理。
您可以为每个视图提供清理方法,例如: .clear()
或.exit()
,可以致电view.undelegateEvents()
和view.stopListening()
。然后确保在完成视图后调用此方法。
如果你小心确保它被调用并确保它清除了所有需要清除的东西,那么它运作良好。
答案 1 :(得分:2)
当您向元素添加另一个视图时,它不会自动删除旧视图;如果你想这样做,你必须明确地呼叫yourFirstView.remove()
。但是,这样做也会删除元素;如果您只想取消绑定事件而不删除元素,则可以使用yourFirstView.undelegateEvents()
代替。
值得一提的是,这是Backbone的故意行为:通常情况下,对单个el
有2个以上的观点会很有帮助。
答案 2 :(得分:0)
当主干使用jQuery(或Zepto ......无论哪个,让我们假设jQuery并让你根据需要替换)将事件绑定到视图中的元素时,它会处理事件,就像事件一样从元素冒出来到el
元素。
此行为来自注释来源中的delegateEvents
:
http://documentcloud.github.com/backbone/docs/backbone.html
delegateEvents: function(events) {
if (!(events || (events = _.result(this, 'events')))) return this;
this.undelegateEvents();
for (var key in events) {
var method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) continue;
var match = key.match(delegateEventSplitter);
var eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateEvents' + this.cid;
if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
}
return this;
},
关键是理解与this.$el.on(eventName, method);
和this.$el.on(eventName, selector, method);
对于jQuery,请参阅http://api.jquery.com/on/,“直接和委派事件”部分。
关于你的代码,希望你能看出它的行为原因。您没有覆盖el
元素本身,因此两个委托绑定仍然有效。
答案 3 :(得分:0)
有时您希望确保视图层次结构不会导致重叠的侦听器绑定。例如,如果你有UL-s的树状视图和子UL-s。这些UL存在于DOM中,您希望将主干视图绑定到某个按钮内; LIS。 那么你应该在">"的帮助下使用更严格的选择器,即代替:
"click li .btn": "handleClick"
你会得到:
"click li > .btn": "handleClick"