This video is available to students only


We learn about d3.transition, and talk about when we would use it instead of CSS transitions. We then update our histogram to animate using d3.transition.


CSS transitions have our back for simple property changes, but for more complex animations we'll need to use d3.transition() from the d3-transition module. When would we want to use d3.transition() instead of CSS transitions?

  • When we want to ensure that multiple animations line up

  • When we want to do something when the animation ends (for example starting another animation)

  • When the property we want to animate isn't a CSS property (remember when we tried to animate our bars' heights but had to use transform instead? d3.translate can animate non-CSS property changes.)

  • When we want to synchronize adding and removing elements with animations

  • When we might interrupt halfway through a transition

  • When we want a custom animation (for example, we could write a custom interpolator for changing text that adds new letters one-by-one)

Let's get our hands dirty by re-implementing the CSS transitions for our histogram.

Let's again start by animating any changes to our bars. Instead of adding a transition property to our styles.css file, we'll start in the chart.js file where we set our barRects attributes.

As a reminder, when we run:

we're creating a d3 selection object that contains all <rect> elements. Let's log that to the console as a refresher of what a selection object looks like.

bars selection

We can use the .transition() method on our d3 selection object to transform our selection object into a d3 transition object.

bars transition

d3 transition objects look a lot like selection objects, with a _groups list of relevant DOM elements and a _parents list of ancestor elements. They have two additional keys: _id and _name, but that's not all that has changed.

Let's expand the __proto__ of our transition object.

__proto__ is a native property of JavaScript objects that exposes methods and values that this specific object has inherited. If you're unfamiliar with JavaScript Prototypal Inheritance and want to read up, the MDN docs are a good place to start.

In this case, we can see that the __proto__ property contains d3-specific methods, and the nested __proto__ object contains native object methods, such as toString().

Transition object, expanded

We can see that some methods are inherited from d3 selection objects (eg. .call() and .each()), but most are overwritten by new transition methods. When we click the Change metric button now, we can see that our bar changes are animated. This makes sense — any .attr() updates chained after a .transition() call will use transition's .attr() method, which attempts to interpolate between old and new values.

Something looks strange though - our new bars are flying in from the top left corner.

Bars flying in

Note that d3 transitions animate over 0.25 seconds — we'll learn how to change that in a minute!

Knowing that <rect>s are drawn in the top left corner by default, this makes sense. But how do we prevent this?

Remember how we can isolate new data points with .enter()? Let's find the line where we're adding new <rect>s and set their initial values. We want them to start in the right horizontal location, but be 0 pixels tall so we can animate them "growing" from the x axis.

Let's also have them be green to start to make it clear which bars we're targeting. We'll need to set the fill using an inline style using .style() instead of setting the attribute in order to override the CSS styles in styles.css.

Why are we using .style() instead of .attr() to set the fill? We need the fill value to be an inline style instead of an SVG attribute in order to override the CSS styles in styles.css. The way CSS specificity works means that inline styles override class selector styles, which override SVG attribute styles.


This page is a preview of Fullstack D3 Masterclass

No discussions yet