10

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

http://jsfiddle.net/feronovak/8svqX/

这没什么特别的,它真的是让我了解如何正确调用单独的视图,所以它们不会触发以前的点击方法。现在我生成了两个视图,第二个视图覆盖了#boxSearch 中的表单。当我单击按钮 Search 时,它也会生成 alert doSearch SearchLocation。我期待只看到 alert 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

4 回答 4

19

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

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

正如@machineghost 指出的那样,您的代码中没有任何内容可以取消绑定第一个视图,因此两个视图都附加到#boxSearch,并且两个视图都将响应该click事件。有几种方法可以解决这个问题:

  • 您可以在事件处理程序中调用e.stopPropagation()docs)(我认为这就是您想要做的e.preventDefault())。这将防止事件一旦被触发就冒泡,因此最后定义的视图会捕获它。这可行,但请注意,您仍然有两个视图,并且过时的幽灵视图可能有其他副作用。

  • 你可以让每个视图用一个 class 渲染它自己的 DOM 元素,.boxSearch然后调用view.remove()你不再需要的那个。这可能是最简洁的选择,但这意味着动态创建大部分 DOM,这比用 HTML 编码更昂贵且难以管理。

  • 您可以给每个视图一个清理方法,例如.clear()or .exit(),它可以调用view.undelegateEvents()and view.stopListening()。然后确保在完成视图后调用此方法。

如果你小心确保它被调用并确保它清除所有需要清除的东西,那么它运行良好。

于 2013-03-26T00:20:08.903 回答
2

当您向元素添加另一个视图时,它不会自动删除旧视图;如果你想这样做,你必须明确调用yourFirstView.remove(). 但是,这样做也会删除该元素;如果您只想取消绑定事件而不删除可以使用的元素yourFirstView.undelegateEvents()

值得一提的是,这是 Backbone 的故意行为:通常,能够在单个el.

于 2013-03-25T23:17:53.527 回答
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元素本身,因此两个委托绑定仍然有效。

于 2013-03-25T23:58:51.293 回答
0

有时您想确保您的视图层次结构不会导致重叠的侦听器绑定。例如,如果您有带有子 UL-s 的 UL-s 的树状视图。这些 UL 存在于 DOM 中,您希望将主干视图绑定到 a;; 内的某个按钮;LI。那么你应该在“>”的帮助下使用更严格的选择器,而不是:

"click li .btn": "handleClick"

你将会拥有:

"click li > .btn": "handleClick"
于 2016-02-15T20:25:12.543 回答