2011-04-24 63 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