2011-04-24 62 views
0

所以我使用的是Backbone.js,在我的一个主要视图中,我遇到了一个非常奇怪的错误,我不能为我的生活弄清楚如何解决。Javascript中的点击绑定问题

该视图看起来像新的Twitter布局。它接收一组对象,其中每个对象都描述一个集合并查看对该集合起作用的元素。每个集合都由视图中的一个选项卡表示。我的视图上的render()方法使用此数组集合对象,清除tabContainer DOM元素(如果它尚未为空),呈现选项卡,然后将事件绑定到每个选项卡。

现在在我的代码中,我有方法来呈现选项卡和方法来按顺序将点击处理程序绑定到这些选项卡。这在第一次执行render()时正常工作,但在随后的render()调用中,click处理程序未被绑定。下面是相关的代码片段:

initialize: function() { 
    // Context on render(), _addAllTabs and _bindTabEvents is set correctly to 'this' 
    _.bindAll(this, 'render', 'openModel', 'closeModel', 'isOpen', 'addAllModels', '_switchTab', 
        'addOneModel', '_addTab', '_removeTab', '_addAllTabs', '_loadCollection', 
        '_renderControls', '_setCurrentCollection', '_loadModels', '_bindTabEvents'); 

    this.template = JST['ui/viewer']; 
    $(this.el).html(this.template({})); 

    // The tabContainer is cached and always available 
    this.tabContainer   = this.$("ul.tabs"); 
    this.collectionContainer = this.$("#collection_container"); 
    this.controlsContainer  = this.$("#controls"); 
    this.showMoreButton   = this.$("#show_more_button"); 
}, 

render: function(collections, dashboard) { 
    // If _bindTabEvents has been called before, then this.tab exists. I 
    // intentionally destroy this.tabs and all previously bound click handlers. 
    if (this.tabs) this.tabContainer.html(""); 
    if (collections) this.collections = collections; 
    if (dashboard) this.$("#dashboard").html(dashboard.render().el); 
    // _addAllTabs redraws each of the tabs in my view from scratch using _addTab 
    this._addAllTabs(); 
    // All tabs *are* present in the DOM before my _bindTabEvents function is called 
    // However these events are only bound on the first render and not subsequent renders 
    this._bindTabEvents(); 
    var first_tab = this.collections[0].id; 
    this.openTab(first_tab); 
    return this; 
}, 

openTab: function (collectionId, e) { 
    // If I move _bindTabEvents to here, (per my more thorough explanation below) 
    // my bug is somehow magically fixed. This makes no friggin sense. 
    if (this.isTabOpen(collectionId)) return false; 
    this._switchTab(collectionId, e); 
}, 

_addAllTabs: function() { 
    _.each(this.collections, this._addTab); 
}, 

_bindTabEvents: function() { 
    this.tabs = _.reduce(_.pluck(this.collections, "id"), _.bind(function (tabEvents, collectionId) { 
    var tabId = this.$("#" + collectionId + "_tab"); 
    tabEvents[collectionId] = tabId.click(_.bind(this._switchTab, this, collectionId)); 
    return tabEvents 
    }, this), {}); 
}, 

_addTab: function(content) { 
    this.tabContainer.append(
    $('<li/>') 
     .attr("id", content.id + "_tab") 
     .addClass("tab") 
     .append($('<span/>') 
     .addClass('label') 
     .text(content.name))); 
    //this._loadCollection(content.id); 
    this.bind("tab:" + content.id, this._loadCollection); 
    pg.account.bind("change:location", this._loadCollection); // TODO: Should this be here? 
}, 

etc.. 

正如我所说的,这里的render()方法的工作,但只在第一次左右。奇怪的是,如果我招行this._bindTabEvents();并使其成为openTab(第一行)方法就像下面的代码片段,那么整个事情完美的作品:

openTab: function (collectionId, e) { 
    this._bindTabEvents(); 
    if (this.isTabOpen(collectionId)) return false; 
    this._switchTab(collectionId, e); 
}, 

当然,这行代码没有业务在该方法的,但它确实使整个事情做工精细,这使我问,为什么在那里工作,但没有像这样连续工作:

this._addAllTabs(); 
this._bindTabEvents(); 

这是没有意义的,我因为如果我把它放在这行之后,它也不起作用:

var first_tab = this.collections[0].id; 

即使这基本上与执行顺序有关的工作相同。

有没有人有任何想法我做错了什么,我应该怎么做才能做到这一点(在行为和编码风格方面)?

+0

它是如何“不工作”?浏览器说什么?什么样的例外?在哪条线上?是否有错误? – Rudie 2011-04-25 00:36:17

+0

这是奇怪的部分。没有例外或没有。一切都打印出来控制台就好了。但是当我点击标签没有事件被解雇。第一次和后续时间之间的clickbind的console.log打印输出的唯一区别在于,jquery对象数组中的元素名称在第一次被灰显,随后他们将元素名称打印成黑色。 – 2011-04-25 05:08:22

回答

0

当你切换选项卡时,你不是简单地显示/隐藏内容,你正在销毁和重建dom元素,所以你也在摧毁附加在它们上面的事件。这就是为什么这些事件只能工作一次,以及为什么要将_bindTabEvents添加到渲染中,因为每次都要重新附加事件。

当这条线执行时:this.tabContainer.html(""); poof ...没有更多的标签和没有更多的标签事件。

+0

正如herostwist提到的,你只是第一次参加这个活动。你可能需要类似于jQuery的live()事件绑定,这可以确保所有现在和将来的DOM元素都被绑定。 – Martin 2011-04-24 21:23:38

+0

这正是我想要的。我想销毁绑定到它们的DOM元素和点击处理程序,因为当我在这个对象上调用render()时,我最终传递了一个新的集合数组和仪表板视图。再看看完整的代码,你会发现你给出的解释在这里不适用。实际上,我在每次执行render()时添加事件。 AFAICT这是一个时间问题。 – 2011-04-24 22:37:31

+0

我不确定我的解释是否清晰,但是当_bindTabEvents在render()方法中时,它只能在第一次运行,而不是在render()的后续执行中运行。 – 2011-04-24 22:40:54

1

在您的视图的渲染功能中,return this.delegateEvents();我认为您在渲染中丢失了事件绑定,需要重新建立它们。

看到该函数的这个链接,Backbone.js的文档: backbone.js - delegateEvents