Магистраль: событие, потерянное при повторной обработке
У меня есть супер-просмотр, кто отвечает за рендеринг под-просмотров. Когда я перерисовываю супер-просмотр, все события в под-представлениях теряются.
Это пример:
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.
Ответы
Ответ 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();
Это позволит вашему представлению успешно выполнить рендеринг с событиями, все еще связанными и работающими.