单击是在骨干中调用两次函数

时间:2013-03-25 23:09:44

标签: templates backbone.js view duplicates

我已经在主干视图上创建了两次警报触发的快速示例。

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();
});

4 个答案:

答案 0 :(得分:19)

这是Backbone应用程序经常出现的“鬼视图”问题:

  • 您将视图附加到DOM元素
  • 您将另一个视图附加到同一个DOM元素
  • 您在DOM元素上触发事件(或者更糟糕的是,在两个视图都听到的其他地方触发事件)
  • 两个视图都会激活他们的事件处理程序,经常造成严重破坏。

正如@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"