在渲染骨干视图时获得通知

Get notified when a Backbone View has been rendered

本文关键字:通知 视图      更新时间:2023-09-26

我正在开发一个Backbone.View,它应该将集合呈现为可滚动列表。

作为初始渲染的一部分,我需要访问一些布局属性(例如clientWidth),这些属性只有在视图渲染后才可用。

我的问题是,我如何知道视图何时被添加到DOM


使用Backbone.View,通常有两种方法将视图附加到DOM:

  1. 创建视图>渲染视图>附加视图:

    view = new MyList().render()
    $('#dummy').append(view.$el)
    
  2. 创建视图并将其渲染到位:

    new MyList({el: '#dummy'}).render()
    

注意:我知道(1)和(2)不完全等价,这不是重点


让我们考虑一下我的列表是这样定义的:

class MyList extends Backbone.View
    render: ->
        @$el->html( ... )
        @
    layout: ->
        max = $el.scrollWidth - $el.clientWidth
        # ... disable / enable scrolling arrows based on max ...

如何确保在MyList附加到DOM后调用layout()?

我遇到过这个问题,有一些模式可以提供帮助。Backbone给你工具,但不会告诉你如何使用它们。在使用过程中建立良好的模式是非常重要的。我使用了两种不同的模式来帮助实现这一点。

1) 根据应用程序的工作方式,在渲染视图并将其附加到DOM后,确保始终调用attached()方法或在视图上触发attached事件可能很容易,也可能很难。在我的情况下,这很容易,因为我创建了一个体系结构,可以在一个中心位置处理它。

2) 如果您总是在渲染后立即将元素附加到DOM,特别是在渲染后的任何时候,而不在异步回调中延迟附加,那么在render()函数中,您可以延迟对layout()或附加后需要运行的任何调用。像这样:

var MyList = Backbone.View.extend({
  render: function() {
    /* rendering logic goes here */
    // Notice that the timeout is 0 - you can actually not even pass this argument
    // but I wanted to emphasize that there is no delay given.
    setTimeout(this.layout, 0);
    return this;
  },
  layout: function() {
    // This code won't be run until after this.el is attached to the DOM
  }
});
var view = new MyList().render();
$('#dummy').append(view.$el);

Javascript现在和将来都是单线程的。I/O可以异步完成,但任何实际需要JS引擎时间的操作都不能与其他JS代码并行运行。当您在没有最后一个参数或值为0的情况下调用setTimeout()时,它实际上会将其排队,以便在当前执行的代码完成后尽快运行。

如果您遵循一种模式,即总是在调用render()后不久(在当前执行的代码完成运行并将控制权返回给JS引擎以运行下一个排队的代码之前的任何时间)附加到DOM,则可以利用这一事实。当setTimeout(func, 0)排队的函数运行时,您就可以知道您的视图已经附加到DOM了。请注意,这与_.defer()所做的是一样的,但我想为您解释一下。

再次强调,模式的建立非常重要。如果你从来没有一致性,也没有在你的应用程序中建立模式,你就没有任何保证,你就失去了依赖这个机制的能力——以及其他你可能能够建立的机制。希望这能有所帮助!

这是一个看似困难的问题。有DOM突变事件,但AFAIK这些并不是跨浏览器实现的,无论如何都不推荐使用。突变观察者似乎是一种新的方法,但同样,我不确定它的兼容性。

一种万无一失但昂贵/混乱的方法是进行民意调查,看看尸体是否在祖先链中。

whenInserted = function ($el, callback) {
  check = function () {
    if ($el.closest('body').length) { // assuming a jQuery object here
      callback();
    } else {
      setTimeout(check, 100);
    }
  };
  check();
};

我对其中一些答案的糟糕程度感到惊讶。JQuery方法是同步的。

所以在Backbone.View渲染功能中,你有几个不错的选择:

//只需调用一个函数

  render:function(){
          //first, use Backbone/jQuery to append to DOM here
           //second 
   this.callSomeFunctionToSignifyRenderIsDone();
      return this;
    }

//触发事件

render:function(){
   //use Backbone/jQuery to append to DOM here

   Backbone.Events.trigger('viewX-rendered'.); //trigger an event when render is complete
  return this;
}

或者,如果您的呈现方法有异步代码,您可以向呈现函数传递一个可选的回调:

render: function(callback){
   //use Backbone/jQuery to append to DOM here
  if(typeof callback === 'function'){
     callback(null,'viewX-rendered'); //invoke the error-first callback
}
}

考虑到从渲染返回"this"以链接主干调用是首选模式,则前两个选项可能是更好的选项。

最后,如果您当前的呈现方法确实是异步的,也就是说,将内容附加到DOM的jQuery代码是异步发生的,那么您可以使用jQuery将侦听器附加到DOM元素,就像一样

https://gist.github.com/azat-co/5898111