Backbone:重新渲染时丢失的事件

IT技术 javascript backbone.js backbone-events
2021-02-28 03:49:30

我有负责渲染子视图的超级视图当我重新渲染超视图中的所有事件的子视图都将丢失。

这是一个例子:

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()再次执行,一切看起来都差不多,但不再触发点击事件

检查工作 jsFiddle

3个回答

当你这样做时:

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渲染时并以这种方式回避问题(但这可能会导致其他问题......)。

我永远不会自己找到它。这有点疯狂:/。谢谢@mu
2021-04-23 03:49:30
@fguillen:我必须找到类似的东西,我认为这涉及到 jQuery 源代码之旅和一些实验。这项努力已将其永久地烙在我的记忆中。
2021-04-23 03:49:30
@muistooshort 类似的问题,但 delegateEvents 没有帮助。jsfiddle.net/ismusidhu/kt9zr5z9/7
2021-04-28 03:49:30
这使我找到了解决问题的正确方向。谢谢@muistooshort
2021-05-02 03:49:30
@IsmailS:您的render调用对视图的this.img属性有什么作用?这是一个提示:如果您使用this.$('#abc')而不是尝试缓存this.img. 折腾了console.log('clicked')在点击处理程序,你会发现你没有一个事件的问题。
2021-05-16 03:49:30

这里有一个更简单的解决方案,它首先不会破坏事件注册: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

非常有趣、更简单的解决方案不是我会使用的世界,但知道它真的很好。谢谢
2021-04-18 03:49:30
我想从创作视图类的角度来看它是否“更简单”是有争议的。我认为这可能更有效,因为您保留了事件侦听器注册而不是每次都重新应用它们。如果您还可以通过jQuery.data()您希望在重新渲染时保留的数据与元素一起存储,这也是一个考虑因素,因为detach()无需额外代码即可保留数据。
2021-04-28 03:49:30

当使用$(el).empty()它会删除在所选择的元件的所有子元素,并删除所有事件被(和数据)束缚于任何(子)单元所选择的元件的内侧(el)。

为了保持绑定到该事件子元素,但还是删除子元素,使用方法:

$(el).children().detach(); 代替 $(.el).empty();

这将允许您的视图在事件仍然绑定和工作的情况下成功重新呈现。

请参阅这些以获取进一步参考:stackoverflow.com/a/7417078/2728686jquerybyexample.net/2012/05/...
2021-04-22 03:49:30