Подтвердить что ты не робот

Магистраль: событие, потерянное при повторной обработке

У меня есть супер-просмотр, кто отвечает за рендеринг под-просмотров. Когда я перерисовываю супер-просмотр, все события в под-представлениях теряются.

Это пример:

var SubView = Backbone.View.extend({
    events: {
        "click": "click"
    },

    click: function(){
        console.log( "click!" );
    },

    render: function(){
        this.$el.html( "click me" );
        return this;
    }
});

var Composer = Backbone.View.extend({
    initialize: function(){
        this.subView = new SubView();
    },

    render: function(){
        this.$el.html( this.subView.render().el );             
    }
});


var composer = new Composer({el: $('#composer')});
composer.render();

Когда я нажимаю кнопку click me div, событие запускается. Если я выполняю composer.render() снова, все выглядит примерно так же, но событие click больше не запускается.

Проверьте рабочий jsFiddle.

4b9b3361

Ответ 1

Когда вы это сделаете:

this.$el.html( this.subView.render().el );

Вы действительно говорите об этом:

this.$el.empty();
this.$el.append( this.subView.render().el );

и empty убивает события во всем внутри this.$el:

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

Итак, вы теряете вызов delegate, который связывает события на this.subView, а SubView#render не будет их перепроверять.

Вам нужно пропустить вызов this.subView.delegateEvents() в this.$el.html(), но вам нужно, чтобы это произошло после empty(). Вы можете сделать это следующим образом:

render: function(){
    console.log( "Composer.render" );
    this.$el.empty();
    this.subView.delegateEvents();
    this.$el.append( this.subView.render().el );             
    return this;
}

Демо: http://jsfiddle.net/ambiguous/57maA/1/

Или вот так:

render: function(){
    console.log( "Composer.render" );
    this.$el.html( this.subView.render().el );             
    this.subView.delegateEvents();
    return this;
}

Демо: http://jsfiddle.net/ambiguous/4qrRa/

Или вы могли remove и повторно создать this.subView при рендеринге и обойти проблему таким образом (но это может привести к проблемы...).

Ответ 2

Здесь есть более простое решение, которое не сдует регистрацию событий в первую очередь: jQuery.detach().

http://jsfiddle.net/ypG8U/1/

this.subView.render().$el.detach().appendTo( this.$el );   

Этот вариант, вероятно, предпочтительнее по соображениям производительности:

http://jsfiddle.net/ypG8U/2/

this.subView.$el.detach();

this.subView.render().$el.appendTo( this.$el );

// or

this.$el.append( this.subView.render().el );

Очевидно, что это упрощение, которое соответствует примеру, когда суб-представление является единственным содержимым родительского элемента. Если это действительно так, вы можете просто повторно отобразить суб-представление. Если бы был другой контент, вы могли бы сделать что-то вроде:

var children = array[];

this.$el.children().detach();

children.push( subView.render().el );

// ...

this.$el.append( children );

или

_( this.subViews ).each( function ( subView ) {

  subView.$el.detach();

} );

// ...

Кроме того, в вашем исходном коде и повторенном в ответе @mu объект DOM передается jQuery.html(), но этот метод документируется только как принятие строк HTML:

this.$el.html( this.subView.render().el );

Документированная подпись для jQuery.html():

.html( htmlString )

http://api.jquery.com/html/#html2

Ответ 3

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

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

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

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