Meteor.js 中更新 DOM 后的回调

Posted

技术标签:

【中文标题】Meteor.js 中更新 DOM 后的回调【英文标题】:Callback after the DOM was updated in Meteor.js 【发布时间】:2012-04-23 23:38:47 【问题描述】:

我有这个 Meteor 项目:https://github.com/jfahrenkrug/code_buddy

它基本上是一个具有大文本区域和预区域的工具,可让您输入源代码 sn-ps 自动推送到所有连接的客户端。

我想在代码更改时自动运行 highlightSyntax 函数,但它并没有真正起作用。

我试过 query.observe,但效果不太好:语法高亮显示一次,然后又消失了。

所以我的问题是:更新 DOM 后如何运行代码?

【问题讨论】:

【参考方案1】:

一个 hacky 方法是:

foo.html

<template name="mytemplate">
  <div id="my-magic-div">
    .. stuff goes here ..
    add_my_special_behavior
  </div>
</template>

foo.js

Template.mytemplate.add_my_special_behavior = function () 
  Meteor.defer(function () 
    // find #my-magic-div in the DOM
    // do stuff to it
  );
  // return nothing
;

该函数将在模板被渲染(或重新渲染)时被调用,因此您可以将其用作挂钩来执行您想要执行的任何特殊 DOM 操作。您需要使用 Meteor.defer(它的作用与 settimeout(f, 0) 相同),因为在渲染模板时,它还没有在 DOM 中。

请记住,您可以在不将模板插入 DOM 的情况下渲染模板!例如,这样做是完全合法的:

console.log(Template.mytemplate())

因此,当一个模板被渲染时,并不能 100% 保证它最终会出现在 DOM 中。这取决于模板的用户。

【讨论】:

我实际上是通过使用 window.setTimeout 解决了它,因为 Meteor.setTimeout 在从 query.observe 调用时会通过异常来解决。 我遇到了一些时间问题。我必须使用几秒钟的非常长的超时,因为在渲染之前似乎仍然存在延迟。 Template.contextualFeed.feedItems = -&gt; Meteor.defer -&gt; console.log "Template.contextualFeed.feedItems delayed" console.log $("abbr.timeago") # -&gt; logs as [], with a additional delay it finds them $("abbr.timeago").timeago() Feed.find() 嗯...也许是这样,因为 Feed.find() 首先需要从 Feed 模型中获取数据?甚至可能是往返?当它获取数据时还没有真正弄清楚...... 这个仍然有效,谢谢!它现在只需要写一点不同: Template.templateName.helpers( add_my_special_behavior: function() ... ); 你可能想要 Meteor 的起源行为。写在这里:github.com/avital/meteor-ui-new-rendered-callback/blob/master/…【参考方案2】:

从 Meteor 0.4.0 开始,Template.myTemplate.rendered 提供了一个回调

在 Template.myTemplate 的实例被渲染到 DOM 节点并第一次放入文档时调用一次

更多信息http://docs.meteor.com/#template_rendered

【讨论】:

这些天并没有真正起作用。见groups.google.com/forum/#!msg/meteor-talk/47Orrrz7kjg/… DOM 更新时不会调用此回调,例如当一个新元素被添加到模板中的#each 时。只有当 Template.myTemplate 的实例被渲染到 DOM 节点并第一次放入文档时,它才会被称为“once。”【参考方案3】:

对于当前版本的Meteor(1.0),我们现在可以使用Tracker的.afterFlush()功能了。

Tracker.autorun(function(e)
   var data = Router.current().data();
   if(data.key !== undefined)
       //the data is there but dom may not be created yet
     Tracker.afterFlush(function()
       //dom is now created.
    );
   
);

【讨论】:

【参考方案4】:

更新 DOM 后没有回调,但是您可以使用 Tracker.flush() 强制所有待处理的 DOM 更新。

致电flush() 后,您知道 DOM 已更新,因此您可以执行任何需要的手动 DOM 更改。

【讨论】:

拥有这种功能真是太好了。 Template.myView.onFlush(function() ) 之类的东西或特殊事件。例如,刚才我正在寻找一种方法来在每次刷新时设置元素的滚动顶部。可能吗?谢谢 @LarZuK 作为 Meteor 开发人员之一(我想我们现在都是开发人员,但我的意思是最初的 4 人团队 :)),我认为这是个好主意,而且我经常想要这样的东西,但我不确定它应该如何工作。如果我在控制台中输入console.log(Template.myView()),你有什么建议吗?在这种情况下永远不要调用该函数? 另一种情况:假设我使用var frag = Meteor.ui.render(Template.myView) 将模板渲染到DOM 节点,然后等待几秒钟,然后才将frag 实际插入到页面中。你认为新的回调应该在模板被渲染到 DOM 节点时触发,还是在那些 DOM 节点被放到屏幕上时触发? (前者容易,后者真的很难……)【参考方案5】:

这个问题很老了,但两年后的解决方案是集成operational transformation library with Meteor 并在客户端使用 Ace 或 CodeMirror,它会自动突出显示语法。这具有允许人们同时进行编辑的额外好处。

我已经为你完成了工作:)

【讨论】:

【参考方案6】:

在Blaze Components(我是作者之一)中,您有一个 API,它在插入、移动或删除 DOM 时调用方法。你可以see here如何在 DOM 发生变化时制作一个响应式变量。

这种方法的缺点是它不会在 DOM 元素属性更改时发生更改(例如 class 更改)。仅当 DOM 元素本身发生更改时。这适用于大多数情况,但如果您需要第二种,我建议您只需使用MutationObserver。在这种情况下,您也可以响应外部变化。

【讨论】:

【参考方案7】:

似乎Template.myTemplate.rendered 无法正常工作或者我不明白...

我需要在渲染所有帖子的模板后内联加载 TinyMCE,所以我有:

- 一个模板

<div id="wrapper">     
         #each posts
             <div class="editable">post</div>
         /each        
  </div>

- 和一个函数

Template.myPosts.rendered = function()
      console.dir($("div"));
      tinymce.init(
          selector: "div.editable",
          inline: true,
          plugins: [
              "advlist autolink lists link image charmap print preview anchor",
              "searchreplace visualblocks code fullscreen",
              "insertdatetime media table contextmenu paste"
          ],
          toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
);

但是,控制台只记录 &lt;div id="wrapper"&gt; 而不是 &lt;div class="editable"&gt; div,其中包含我的帖子。所以,似乎Template.myTemplate.rendered 回调发生在模板渲染之前,对吧?

编辑:我将Template.myTemplate.rendered 代码放在setTimeout() 中,似乎一切正常,所以我确信Template.myTemplate.rendered 会导致问题。

【讨论】:

【参考方案8】:

我刚刚发现了一个看起来运行良好的小技巧:

Template.myTemplate.onRendered(function() 
    this.autorun(function() 
        Meteor.setTimeout(function() 
            // DOM has been updated
        , 1);
    );
);

我不是 Meteor 专家,所以它可能有一些缺点,但我目前还没有发现任何缺点 - 除了它有点脏!

【讨论】:

【参考方案9】:

我想你可能想将回调传递给

Meteor.startup(callback)

见http://docs.meteor.com/#meteor_startup

【讨论】:

谢谢,但这并不是要知道 DOM 何时在页面加载时准备就绪。由于来自服务器的数据更改,我需要知道 DOM 何时完成更新。

以上是关于Meteor.js 中更新 DOM 后的回调的主要内容,如果未能解决你的问题,请参考以下文章

Vue.nextTick DOM 更新循环结束之后执行延迟回调

Vue中nextTick的正确使用

this.$nextTick()

Vue.nextTick(callback)

Vue.nextTick(callback)

vue.nextTick()方法的使用详解