1

我正在研究一个Backbone.View应该将集合呈现为可滚动列表的方法。

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

我的问题是,我怎么知道视图何时被添加到 DOM 中?


使用 aBackbone.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()?

4

3 回答 3

9

我遇到了这个问题,并且有一些可以提供帮助的模式。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 代码并行运行。当您在setTimeout()没有最后一个参数或它的值的情况下调用时,0实际上会将其排队等待在当前执行的代码完成后尽快运行。

如果您遵循始终在调用后立即附加到 DOM 的模式,则可以利用这一事实render()——在当前执行的代码完成运行并将控制权返回给 JS 引擎以运行接下来排队的任何内容之前的任何时间。当排队的函数setTimeout(func, 0)运行时,您就可以知道您的视图已附加到 DOM。请注意,这与 what _.defer()does 相同,但我想为您解释一下。

再一次,模式的建立非常重要。如果您从来没有保持一致并且没有在应用程序中建立模式,那么您就没有任何保证,并且您将失去依靠这种机制的能力——以及您可能能够建立的其他机制。希望这可以帮助!

于 2013-04-11T07:00:02.650 回答
3

这是一个看似困难的问题。有DOM Mutation Events,但是 AFAIK 这些没有跨浏览器实现,并且无论如何都被弃用了。Mutation Observers似乎是执行此操作的新方法,但同样,我不确定兼容性。

一个万无一失但昂贵/混乱的方法是轮询以查看身体是否在祖先链中的某个位置。

whenInserted = function ($el, callback) {
  check = function () {
    if ($el.closest('body').length) { // assuming a jQuery object here
      callback();
    } else {
      setTimeout(check, 100);
    }
  };
  check();
};
于 2012-09-05T16:44:22.280 回答
0

我对其中一些答案的糟糕程度感到惊讶。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”以链接 Backbone 调用是首选模式,那么前两个选择可能是更好的选择。

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

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

于 2015-07-13T06:43:47.713 回答