Animations in Meteor
State of the game
UPDATE: David Greenspan posted some detailed info about the heuristics that meteor uses to update DOM nodes. The important takeaway: you can achieve animations the ‘meteor way’ if you set an id on your DOM nodes (or a name on form elements). More details below.
A common question that people have as they begin to use Meteor for developing a javascript application is how to implement animations. This isn’t surprising as the example applications don’t really use them and the docs make no mention of it. Yet for a client-side responsive JS app, animating JS changes makes a lot of sense.
In this post I’d like to discuss the animation of a simple expanding div, as seen below.
Using CSS Transitions
The HTML5 way to achieve this is using the CSS3 transition property. CSS transitions work when the class changes on an element. The browser will animate the transition of a CSS property between the old value (as specified by the first class) and the new.
Reactively: the Meteor way
It fits with Meteor’s reactive setup to force the class on an element to change via a variable change. For instance, with the correct CSS setup, we could do something like:
<div class="{{open}}" id="clickme">Click me!</div>
Template.foo.open = function() { if (Session.get(‘open’)) { return ‘open’; }};
Template.foo.events = {‘click #clickme’: function() {
Session.set(‘open’, true);
}};
The only challenge in this technique is naming the session variable to use to store the state of the <div>
[1]. However, it’s usually sensible to call it something like this._id + '-open'
, depending on exactly what you are doing. In the future, meteor will have ways to attach reactive variables to the “template invocation” that’s currently rendering—this will avoid using the session in ways that it’s not really intended for.
Please note that this technique only works if you set an id on your <div>
. This is because it isn’t easy for meteor to tell when you’ve simply changed the class on an element—from meteor’s perspective you may have deleted the original <div>
and created a totally new <div>
which is identical except for the classname change. Setting an id tells meteor that, no, in fact that is the same element after all. (Similarly, you can just set the name attribute on a form element).
Directly: the jQuery way
The more direct, jQuery way to do it is to simply go
Template.foo.events = {‘click .somewhere’: function(e) {
$(e.target).addClass(‘open’);
}};
This technique will work, however, be very careful. If for some reason the template needs to be reactively re-drawn (for instance the underlying data is updated), the class-name will be lost as the element will be re-rendered. There are techniques to try and isolate meteor’s re-rendering (for instance using Meteor.ui.chunk
in a helper, the “{{#each}}` helper, or a using separate template). However, you are basically swimming upstream with this approach; for this reason I think doing things this way is likely to be a maintenance nightmare. Use at your own risk.
What other techniques are people using for animation?
[1]: the nice thing about using a session var is that the <div>
will remain open if there is a hot-code change. If this isn’t what you want, you might consider adding a reactive variable to your data object.
Comments