【问题标题】:Click is calling function twice in backboneClick 在主干中调用函数两次
【发布时间】:2013-03-25 23:09:43
【问题描述】:

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

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

这没什么特别的,它真的是让我了解如何正确调用单独的视图,这样它们就不会触发之前的点击方法。现在我生成了两个视图,第二个视图覆盖了#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();
});

【问题讨论】:

    标签: templates backbone.js view duplicates


    【解决方案1】:

    这是 Backbone 应用程序中常见的“幻像”问题:

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

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

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

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

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

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

    【讨论】:

    • 这是一个很好的答案。它有非常好的细节,并解释了为什么它是一个问题以及如何解决它。非常感谢。
    【解决方案2】:

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

    值得一提的是,这是 Backbone 的故意行为:通常能够在单个 el 上获得 2 次以上的浏览量是有帮助的。

    【讨论】:

    • 注意:不要在这里使用view.remove() - 这会从DOM中删除#boxSearch元素,而不仅仅是取消绑定视图。
    【解决方案3】:

    当主干使用 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 元素本身,因此两个委托绑定仍然有效。

    【讨论】:

      【解决方案4】:

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

      "click li .btn": "handleClick"
      

      你会:

      "click li > .btn": "handleClick"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多