события просмотра магистрали не работают после повторного рендеринга

Я рву на себе волосы, кажется, я не могу заставить события мыши работать на моем магистральном представлении после повторного рендеринга представления, если только я не сделаю самую нелепую вещь:

$("a").die().unbind().live("mousedown",this.switchtabs);

На самом деле у меня это было, но я решил обновиться до последней версии и попробовать использовать новую функцию delegateEvents().

Вот как структурирован мой идентификатор проекта:

Appview / AppRouter
  |
  ----->PageCollection
             |
             ------->PageView/PageModel
             ------->PageView/PageModel      these page view/models are not rendered
             ------->PageView/PageModel
             |
             ------->PageView/PageModel
                        |
                        ----->render()      *when a pageview is rendered*
                                |
                                -----> Creates new 
                                     Tabcollection
                                       |
                                       --->TabModel/TabView   <-- this is where the issue is 

Что происходит, так это то, что коллекция вкладок имеет основное представление вкладок для управления всеми вкладками, затем создает новую модель/представление для каждой вкладки и помещает прослушиватель для повторного рендеринга представления вкладок всякий раз, когда вкладка загружается. Если вкладка повторно отображается, никакие события мыши больше не работают, если я не добавлю туда этот надуманный оператор jQuery.

Вот вкладка и рендер (я немного урезал его)

var TabPanelView = Backbone.View.extend({
    className:      "tabpanel",
    html:           'no content',
    model:          null,
    rendered:       false,
    events:{
        'click a.tab-nav': 'switchtabs'
    },
    initialize: function(args)
    {
        this.nav            = $("<ol/>");
        this.views          = args.items;
        this.className      = args.classname?args.classname:"tabpanel";
        this.id             = args.id;
        this.container      = $("<section>").attr("class",this.className).attr("id",this.id);
        _.bindAll(this);
        return this.el
    },
    /*
    This render happens multiple times, the first time it just puts an empty html structure in place
    waiting for each of the sub models/views to load in (one per tab)
    */
    render: function(args){
        if(!args)
        {
            //first render
            var nav             = $("<aside/>").addClass("tab-navigation").append("<ol/>").attr("role","navigation");
            var tabcontent      = $("<section/>").addClass("tab-panels");
            for(i = 0;i<this.views.length;i++)
            {
                $("ol",nav).append("<li><a rel='"+this.views[i].id+"' href='javascript:;' class='tab-nav'></a></li>");
                tabcontent.append(this.views[i].el);
            }
            this.$el.empty().append(nav).append(tabcontent);
        }
        else if(args && args.update == true){
            // partial render -- i.e. update happened inside of child objects
            var targetid = args.what.cid;
            for(i = 0;i<this.views.length;i++)
            {
                var curcontent  = this.$el.find("div#"+this.views[i].id);
                var curlink     = this.$el.find("a[rel='"+this.views[i].id+"']")
                if(this.views[i].cid == targetid)
                {
                    curcontent.html($(this.views[i].el).html());
                    curlink.text(this.views[i].model.rawdata.header);
                }
                if(i>0)
                {
                    // set the first panel 
                    curcontent.addClass("tab-content-hide");
                }
                if(i==0)
                {
                    curcontent.addClass("tab-content-show");
                    curlink.addClass("tab-nav-selected");
                }
                // this ridiculous piece of jQuery is the *ONLY* this i've found that works
                //$("a[rel='"+this.views[i].id+"']").die().unbind().live("mousedown",this.switchtabs);
            }
        }
        this.delegateEvents();
        return this;
    },
    switchtabs: function(args){

        var tabTarget = args.target?args.target:false
        if(tabTarget)
        {
            this.$el.find("aside.tab-navigation a").each(function(a,b)
            {
                $(this).removeClass("tab-nav-selected")
            })
            $(tabTarget).addClass("tab-nav-selected");
            this.$el.find("div.tab-content-show").removeClass("tab-content-show").addClass("tab-content-hide");
            this.$el.find("div#"+tabTarget.rel).removeClass("tab-content-hide").addClass("tab-content-show");
        }
    }
});

Кто-нибудь может подумать, почему события мыши в магистрали просто не срабатывают вообще, потому что они не находятся в DOM? Я думал, что именно здесь магистраль может быть особенно полезной?...


person Alex    schedule 14.03.2012    source источник
comment
Какие шаги вы уже предприняли для устранения этой проблемы? Я бы попробовал пару вещей: Посмотрите, можете ли вы установить точку останова в исходном коде Backbone, где обрабатывается элемент events. Может дать вам некоторое представление. Кроме того, попробуйте использовать точки останова Chrome DOM и Event Listener. Это может помочь вам проверить, выполняется ли вообще какой-либо код или события просто не подключаются.   -  person Josh Earl    schedule 14.03.2012
comment
Я зарегистрировался в jq $(document).find(tab-nav-selected), чтобы увидеть, действительно ли визуализированные элементы находятся в доме. Хорошо, получайте правильные результаты. прошли через точки останова в моем собственном коде. Я недостаточно уверен в этом, чтобы копаться в самой магистрали с помощью отладчика. с областью видимости все в порядке... Я никоим образом не уверен, но кажется, что события либо перезаписываются из-за повторного рендеринга, либо... эммм   -  person Alex    schedule 14.03.2012


Ответы (3)


Эта строка кода, вероятно, является вашей проблемой:

this.delegateEvents();

Удалите это, и это должно работать.

Единственный раз, когда вам нужно вызвать delegateEvents самостоятельно, это когда у вас есть события, объявленные отдельно от хэша events вашего представления. Представление Backbone вызовет этот метод для вас, когда вы создадите экземпляр представления.

person Derick Bailey    schedule 14.03.2012
comment
к сожалению нет кубика. для меня большая честь, что вы ответили, я прочитал кучу полезных материалов в вашем блоге. Я сдался и ушел с jQuery.live... да, это мерзость... но это единственное, что работает, и у меня есть 2 дня до крайнего срока... хммм - person Alex; 14.03.2012

Когда представление повторно визуализируется, вы повторно используете одно и то же представление и просто снова вызываете для него функцию render(), или вы удаляете представление и создаете совершенно новое представление?

В любом случае, похоже, что причина в том, что события представления не освобождаются до повторной визуализации представления. У Дерика Бейли есть отличный пост. об этом.

При повторном рендеринге: 1) убедитесь, что вы отвязываете все события в старом представлении и 2) создаете новое представление и визуализируете его.

person Paul    schedule 14.03.2012
comment
хорошая статья, однако я не могу создавать новые родительские представления, когда обновляется каждое дочернее представление (родительское представление должно реагировать на дочернее представление). Как ни странно, я попробовал метод, описанный в статье, однако, как ни странно, он работает примерно 1 из каждых 3 раз, когда я перезагружал страницу в другие разы... ничего - person Alex; 14.03.2012

При использовании $(el).empty() он удаляет все дочерние элементы в выбранном элементе И удаляет ВСЕ события (и данные), которые привязаны к любым (дочерним) элементам. strong> внутри выбранного элемента (el).

Чтобы сохранить привязку событий к дочерним элементам, но при этом удалить дочерние элементы, используйте:

$(el).children().detach(); вместо $(.el).empty();

Это позволит вашему представлению успешно выполнить повторную визуализацию с событиями, все еще связанными и работающими.

person AmpT    schedule 12.03.2014
comment
@Alex В своем коде попробуйте: this.$el.children().detach().append(nav).append(tabcontent);, и он должен работать отлично. Нет необходимости воссоздавать экземпляр представления. - person AmpT; 12.03.2014
comment
См. эти ответы в качестве дополнительной ссылки: stackoverflow.com/a/12029250/2728686 и stackoverflow.com/a/7417078/2728686 - person AmpT; 12.03.2014