Running arbitrary javascript immediately after reactive DOM update


#1

See this stack overflow question by someone else for a similar issue: http://stackoverflow.com/questions/28069808/meteor-call-js-after-dom-changed-by-reactive-template-data?noredirect=1#comment48987262_28069808

I have an app with a bunch of tabs. When a tab is selected, the corresponding section of body becomes visible by a reactive change to a HTML class. When this happens, I want to focus() a text input box. However, because the section of body was display: none, and because you can’t call focus on something that has display:none set, there is no right time for me to call focus().

The successful workaround is to put a setTimeout for 50ms in the code that makes the change to the session variable to cause the reactive change, but obviously that’s not the right way to do anything.


#2

I have resolved similar problem in the past like this :

// do you stuff that will trigger the reactive change then
Tracker.flush();
Meteor.defer(function(){
  //then do your "later" stuff
});

#3

I think this is essentially what Tracker.afterFlush is designed for. Just put your focus code in the afterFlush callback, and the DOM should have updated by the time it runs (provided the relevant computation has actually been invalidated when you call afterFlush).


#4

I wrote the originally SO question, thanks xaxxon for following it up on here

The manual Tracker.Flush forces the updates and triggers re-rendering of the UI that is affected but I’m not sure if what is within the defer block could still be called too early?

According to this - https://github.com/oortcloud/unofficial-meteor-faq#user-content-how-do-i-animate-things-addingbeing-removed-from-collections Meteor.defer() is essentially a setTimeout of 0 so it may still be too soon for the DOM to have fully updated? It was a while ago but I had experienced timing issues with this before.


#5

I put my focus code in a Tracker.afterFlush and it works beautifully. Thank you for your help everyone.


#6

Actually there is a somewhat similar example [in the docs] (http://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe):

Another example where you want to initialize a plugin when the subscription is done:

Template.listing.onRendered(function () {
  var template = this;

  template.subscribe('listOfThings', function () {
    // Wait for the data to load using the callback
    Tracker.afterFlush(function () {
      // Use Tracker.afterFlush to wait for the UI to re-render
      // then use highlight.js to highlight a code snippet
      highlightBlock(template.find('.code'));
    });
  });
});