为什么在我的骨干应用中多次触发点击事件

Why is the click event triggered several times in my Backbone app?

本文关键字:事件 我的 应用 为什么      更新时间:2023-09-26

我正在制作一个Backbone.js应用程序,它包括一个索引视图和几个基于id的子视图。所有视图都绑定了鼠标按下和鼠标向上事件。但是每次我从一个子视图转到索引视图,然后再次转到任何一个子视图时,当前子视图中的鼠标按下和鼠标向上事件都会再次触发,这意味着当我单击子视图时,会触发几个连续的鼠标按下事件,然后触发几个连续的鼠标向上事件。

在查看了我的代码后,我终于发现是路由器导致了这个问题。我的部分代码如下:

    routes: {
        "": "index",
        "category/:id": "hashcategory"  
    },
    initialize: function(options){
        this._categories = new CategoriesCollection();
        this._index = new CategoriesView({collection: this._categories});
    },
    index: function(){
        this._categories.fetch();
    },
    hashcategory: function(id){
        this._todos = new TodosCollection();
        this._subtodolist = new TodosView({ collection: this._todos, 
                                            id: id                                              
                                         });
        this._todos.fetch();
   }

如您所见,我在路由器的 initialize 方法中创建索引集合和视图,但我在路由器对应的路由函数中创建子视图集合和视图。我尝试将索引集合和视图放在index函数中,索引视图中的单击事件的行为方式与子视图相同。所以我认为这就是为什么鼠标按下和鼠标向上会被触发几次的原因。

但问题是,我必须使用 id 作为发送到子视图的参数之一。所以我无法在initialize方法中创建子视图。更重要的是,我已经看到了其他人基于 Backbone 的项目,其中一些还在相应的路由函数中创建子集合和视图,但他们的应用程序运行良好。所以我不知道我问题的根源是什么。有人可以给我一些想法吗?

听起来您遇到了委托问题,因为:

所有子视图都使用相同的<div>元素

主干视图在其el上使用jQuery的delegate绑定到事件。如果视图使用 <div> 作为其el,然后通过替换包含的 HTML 对另一个视图使用相同的<div>,则最终将通过两个不同的delegate调用将两个视图附加到该<div>。如果再次交换视图,您将有三个视图通过三个delegate接收事件。

例如,假设我们有这个 HTML:

<div id="view-goes-here"></div>

以及这些观点:

var V0 = Backbone.View.extend({
    events: { 'click button': 'do_things' },
    render: function() { this.$el.html('<button>V0 Button</button>'); return this },
    do_things: function() { console.log('V0 clicked') }
});        
var V1 = Backbone.View.extend({
    events: { 'click button': 'do_things' },
    render: function() { this.$el.html('<button>V1 Button</button>'); return this },
    do_things: function() { console.log(V1 clicked') }
});

我们用这样的东西在它们之间切换(当然which从 0 开始(:

which = (which + 1) % 2;
var v = which == 0
      ? new V0({el: $('#view-goes-here') })
      : new V1({el: $('#view-goes-here') });
v.render();

然后,您将遇到我上面描述的多代表问题,并且此行为似乎与您描述的症状相匹配。

下面是一个便于查看的演示:http://jsfiddle.net/ambiguous/AtvWJ/

解决此问题的一种快速简便的方法是在呈现新视图之前对当前视图调用undelegateEvents

which = (which + 1) % 2;
if(current)
    current.undelegateEvents(); // This detaches the `delegate` on #view-goes-here
current = which == 0
      ? new V0({el: $('#view-goes-here') })
      : new V1({el: $('#view-goes-here') });
current.render();

演示:http://jsfiddle.net/ambiguous/HazzN/

更好的方法是为每个视图提供自己独特的el,以便在替换 HTML 时所有内容(包括delegate绑定(都会消失。您最终可能会得到很多<div><div>real stuff</div></div>结构,但这不值得担心。