Release 2.0.0

189 views
Skip to first unread message

Mike Bostock

unread,
Aug 24, 2011, 5:16:40 PM8/24/11
to d3...@googlegroups.com
https://github.com/mbostock/d3/wiki/Release-Notes

2.0.0 - August 23, 2011

Enter and Update

The enter-update pattern has been simplified: the enter selection now
merges into the update selection when you append or insert. This new
approach reduces code duplication between enter and update. Rather
than applying operators to both the enter and update selection
separately, you can now apply them to the update selection after
entering the nodes.

For example, say you had a selection of circles and wanted to update
their radii. Previously you had to call the attr operator twice, once
for enter and once for update:

var circle = svg.selectAll("circle").data([data]);
circle.exit().remove();
circle.enter().append("svg:circle").attr("r", radius); // for enter
circle.attr("r", radius); // for update

In addition, if you wanted circle to refer to all the on-screen nodes
(enter ∪ update) subsequently, you'd have to reselect as well to merge
the enter and update selections:

circle = svg.selectAll("circle"); // reselect

In 2.0.0, you can eliminate this duplicate code because entering nodes
will add them to both the enter selection and the update selection
simultaneously. Running operators on the update selection after enter
will thus apply to both entering and updating nodes:

var circle = svg.selectAll("circle").data([data]);
circle.exit().remove();
circle.enter().append("svg:circle"); // adds enter to update
circle.attr("r", radius); // for enter and update

Note: in the rare case that you want to run operators only on the
updating nodes, you can run them on the update selection before
entering new nodes.

Selector Functions

The select and selectAll operators can now take selector functions, in
addition to selector strings such as "#id" and ".class". For example,
if you want to select the first child of every element, you can now
say:

var children = g.select(function() { return this.firstChild; });

This also means that selection can dynamic create new elements, or
reorder existing elements by re-inserting them into the DOM. This is
an advanced feature, but you might find it useful for extending D3.
For example, you could use XPath rather than selectors if you wanted.

Transparent Transitions

Transitions are now transparent arrays of elements, and you can
inspect them in the developer console just like selections. Each
selected element is wrapped in an object that stores the delay and
duration of the associated transition. (Recall that these values are
computed on a per-element basis for staggered animations.) Internally,
some of the timing logic that manages transitions has also been
simplified, improving performance and fixing a few timing bugs.

The each operator can now be called with one argument (a callback
function), offering compatibility with the selection's each operator.
Transitions now expose an id property, which can be useful for
debugging concurrent transitions; this identifier is inherited by
subtransitions, fixing a bug with nested transitions.

Custom Tweens

A new, generic tween operator has been added, which is used internally
by the other tweens (style, attr, etc.). You can use this operator
directly if you want to define a custom tween as part of the
transition; use this instead of listening for a transition "tick"
event. For example, the text operator does not interpolate by default,
but you can now interpolate text content by saying:

selection.transition().tween("text", function() {
var i = d3.interpolate(this.textContent, "yellow");
return function(t) {
this.textContent = i(t);
};
});

You might want to write your tweens as reusable functions (say,
closures) rather than the above example which hard-codes the
transition to "yellow". See the built-in attr and style tweens for
inspiration.

Axes

A new axis component has been added to the d3.svg module. The axis
component makes it easy to add reference lines, ticks and labels to
any visualization. This display of the axes is highly customizable,
and best of all, the axes support smooth transitions automatically.
See this quick demo of axes used by an area chart:

http://bl.ocks.org/1166403

More documentation and examples for this component will be coming in
the new few days.

Extending and Overriding

Selection and transition are now defined using prototype injection
rather than direct extension. This improves performance and reduces
memory overhead, as the majority of methods are now defined on a
prototype rather than on each instance. Also, this makes the code
cleaner as each operator is fully separable and defined in its own
source file. This fixed a few bugs, such as the missing empty operator
on enter selections.

Prototype injection also means that selections and transitions can be
extended and customized! You can now override the behavior of D3's
core operators, or add your own. This may be particularly useful to
provide compatibility with nonstandard browsers, or proprietary
document object models. You can also use JavaScript's instanceof
operator to see whether an object is a d3.selection or d3.transition.

Tests

D3 now has an extensive test suite built with Vows. As of the 2.0.0
release, we have 1,200+ tests and 90% coverage of the core library.
More tests are under development. The tests are written to test the
behavior of each of D3's operators, and may be interesting to explore
if you have questions about how D3 works, complementing the API
reference.

Phrogz

unread,
Aug 24, 2011, 5:40:44 PM8/24/11
to d3-js
On Aug 24, 3:16 pm, Mike Bostock <mbost...@cs.stanford.edu> wrote:
> 2.0.0 - August 23, 2011

Congratulations, and thank you!

> The enter-update pattern has been simplified: the enter selection now
> merges into the update selection when you append or insert.

And thank you, and thank you! :)

Hilton Perantunes

unread,
Aug 24, 2011, 5:46:15 PM8/24/11
to d3...@googlegroups.com
Congrats! :)
--
Hilton William Ganzo Perantunes
--
Il y a loin de la coupe aux lèvres.

Benjamin West

unread,
Aug 24, 2011, 5:54:27 PM8/24/11
to d3...@googlegroups.com
Love the axis example!
-bewest

Mike Bostock

unread,
Aug 24, 2011, 9:25:14 PM8/24/11
to d3...@googlegroups.com
Oh, and one more thing:

Sequenced transitions are now also easier to implement, thanks to the
transition's transition operator, which returns a copy of the current
transition. The copy inherits the delay, duration, id and easing of
the original transition. You can then modify the delay to sequence
multiple transitions, without needing to listen for the "end" event.
For example, here's how you would enter a circle, and then remove it
after a couple seconds:

svg.append("svg:circle")
.attr("r", 1e-6)
.transition()
.ease(Math.sqrt)
.attr("r", 4.5)
.transition()
.delay(2000)
.attr("r", 1e-6)
.remove();

You can also use this technique to use different easing functions for
different tweens! For example, you could use "cubic-in-out" easing for
position properties, and "linear" for color.

Mike

Jon Frost

unread,
Aug 24, 2011, 10:12:24 PM8/24/11
to d3...@googlegroups.com
The Axis, Transition, and Selector changes sound really good, but good
lord,
just when I was starting to feel that I understood most of what D3 offers.

I think we just need someone to 2nd my old feature requests:
d3.time.scale.revert()
d3.time.scale.delay()
d3.time.scale.extender()

{:-) Really nice work.


Bixente

unread,
Aug 25, 2011, 8:36:42 AM8/25/11
to d3-js
Thank you Mike!
Bixente

Ricardo

unread,
Aug 25, 2011, 11:07:16 AM8/25/11
to d3-js
Excellent Mike!!! The enter/update selection and the axes are a great
improvement in the expressiveness of d3.

Chris Viau

unread,
Aug 25, 2011, 1:58:09 PM8/25/11
to d3...@googlegroups.com
Thanks for the update. The unit tests are very useful as a discovery tool. 
But for the new transition chaining, you still have to listen to the end event if you want to chain them, no? For example:

svg.append("svg:circle") .attr("r", 10) .transition() .duration(1000) .attr("r", 20) .transition() .duration(1000) .attr("r", 40);

Will not delay the second transition one full second. You will still need to use a workaround like:

svg.append("svg:circle") .attr("r", 10) .transition() .duration(1000) .attr("r", 20) .each("end", animateSecondStep); function animateSecondStep(){ d3.select(this) .transition() .duration(1000) .attr("r", 40); };

Am I right?

P.S.: I suppose your "and one more thing" post intro is a tribute to Steve Job?

Mike Bostock

unread,
Aug 25, 2011, 2:25:39 PM8/25/11
to d3...@googlegroups.com
> But for the new transition chaining, you still have to listen to the end
> event if you want to chain them, no?

Correct. I didn't want to assume that the chained transition runs at
the end; you can use transition.transition() to create concurrent
transitions on the same elements as well. For example, you might delay
a transition on some attributes, or use different easing functions for
different attributes.

You don't need to use the "end" event to sequence transitions if you
bake the previous duration into the delay:

svg.append("svg:circle")
.attr("r", 10)
.transition()
.duration(1000)
.attr("r", 20)
.transition()

.delay(1000)
.attr("r", 40);

Note that you don't need to specify the duration twice because it's
inherited from the previous transition. If you wanted to do this
automatically, you could write a plugin that inherits the delay as the
previous transition's delay + duration, like so:

https://gist.github.com/1171371

Perhaps we could support this by saying .transition("end") rather than
.transition().

Mike

Chris Viau

unread,
Aug 25, 2011, 2:33:57 PM8/25/11
to d3...@googlegroups.com
How can you come up with so much solutions so fast. Is Mike Bostock the name of an army of Stanford students? Or of a coding robot? Thanks!

Mike Bostock

unread,
Aug 25, 2011, 2:52:58 PM8/25/11
to d3...@googlegroups.com
> Is Mike Bostock the name of an army of Stanford students?

Haha. Turns out, I'm just a proxy on top of Amazon's Mechanical Turk. ;)

Mike

Chris Viau

unread,
Aug 25, 2011, 2:55:31 PM8/25/11
to d3...@googlegroups.com
Another good idea! Thanks ;)

Davin

unread,
Aug 25, 2011, 4:34:37 PM8/25/11
to d3...@googlegroups.com
Tremendous work Mike!  Really looking forward to using all the new features.  Thank you.

Jeff Bordogna

unread,
Aug 27, 2011, 5:09:31 PM8/27/11
to d3...@googlegroups.com
Great work - thanks for such a great toolkit!

Just a note that updating to 2.0.0 broke a piece of our code that uses ordinal scales.  It seems that the new version of d3.scale.ordinal() automatically converts its domain values to strings, which means that using dates in the domain now won't work.  Also might cause similar issues with other non-string keys such as integers or floats (though javascript is friendlier with auto-converting these back from the string representation).  Migrating our code to the time scale should be OK, or using the ordinal scale with timestamps instead of dates - just thought you should know about this potential migration issue.

Check out the Crimean war example on the wiki to see the problem in action (dies when trying to render the labels, because it expects date format, not string):


Cheers,
Jeff

Mike Bostock

unread,
Aug 27, 2011, 7:16:55 PM8/27/11
to d3...@googlegroups.com
> It seems that the new version of d3.scale.ordinal() automatically
> converts its domain values to strings, which means that using
> dates in the domain now won't work.

Thanks for the report. I overlooked this use case; I was simply trying
to make the external behavior match the internal behavior, since in
order to determine uniqueness, the ordinal scale must coerce the
domain values to strings. However, perhaps it was overly aggressive to
coerce the domain types to strings; it seems reasonable to leave the
values as they are and merely rely on the toString behavior to
determine uniqueness. I'll fix this in the next release.

Mike

Ivan

unread,
Aug 28, 2011, 5:57:15 PM8/28/11
to d3...@googlegroups.com
Awesome 2.0. I love the new axis component.

Cheers,
Ivan

Davin

unread,
Aug 28, 2011, 9:10:16 PM8/28/11
to d3...@googlegroups.com
Hi Mike, could you explain a little bit more about the changes to transition()?  I updated to 2.0 and at first there appeared to be a severe performance penalty with all the transitions in my chart.  I tracked it down to my extensive use of .duration(0).  I switch between realtime updating of my chart and using a transition by using transition().call() on a function that decides which kind of transition to use:

function(selection) {
    if (animateable) {
        selection.duration(200);
    } else {
        selection.duration(0);
    }
}

This probably isn't the most efficient way of doing this, but it was effective... that is until moving to 2.0 and now all the cases where duration(0) is used causes extreme stuttering.  Can you suggest a better way of doing this?

Thanks!
Davin

Mike Bostock

unread,
Aug 28, 2011, 9:26:24 PM8/28/11
to d3...@googlegroups.com
Which version are you using, exactly? There was a small fix to
transition timing, to provide more consistent timing across elements.
This might also change the behavior for zero-duration transitions. The
current release is 2.0.4.

https://github.com/mbostock/d3/commit/4e96a35b6616f4d2a16199e9614368143888e434

One of the underlying changes in v2 is that each node now has a
separate timer when the transition starts. This improves performance
for variable-delay and variable-duration transitions, because now the
timers can stop when each node finishes, rather than sharing a single
timer than runs from min(start) to max(end).

Looking closely, I see another side-effect of simplifying the
transition logic in v2: it now takes at least two ticks to end a
zero-duration transition, whereas previously it took one tick. This is
because the transition starts after one tick, but we don't check to
see if the transition ends until the next tick; this probably explains
your stuttering. I think we could make a tiny change to transition.js
such that a tick is processed immediately on start, such that
zero-duration transitions end after a single tick. Something like
this:

if (!tick(elapsed)) d3.timer(tick, 0, then);

I'll see about including this in the next patch release (today-ish).

Regardless, you really shouldn't be using zero-duration transitions if
you want to avoid stuttering and/or care about performance. Even one
tick is bad, because it means that the browser redraws both before and
after the tick. One technique that I like is a method that optionally
derives a transition, or else returns the input selection:

function transition(selection, duration) {
return duration ? selection.transition().duration(duration) : selection;
}

You can see a similar technique in use in the new axis component:

https://github.com/mbostock/d3/blob/master/src/svg/axis.js#L95-100

Mike

Davin

unread,
Aug 28, 2011, 9:52:46 PM8/28/11
to d3...@googlegroups.com
I'm using 2.0.4.  Your explanation makes sense and after switching to your transition technique, performance is back if not better.

Thanks Mike!

Mike Bostock

unread,
Aug 29, 2011, 3:23:37 PM8/29/11
to d3...@googlegroups.com
Fixes for ordinal scales, transitions, and IE9 are now available in 2.1.1:

https://github.com/mbostock/d3/compare/v2.0.0...v2.1.1

Mike

Jeff Bordogna

unread,
Aug 29, 2011, 5:09:29 PM8/29/11
to d3...@googlegroups.com
Ordinal scale stuff works great again - thanks Mike for the quick turnaround!

Davin

unread,
Aug 30, 2011, 1:30:46 PM8/30/11
to d3...@googlegroups.com
Hi Mike, I happened upon another problem with transitions... It seems that under certain conditions, the transitions don't finish the animation.  It's as if they just needed one more frame and stopped before it rendered.  It doesn't happen with simple animations, but with my more complex ones where there's a lot of movement and the enter/exit selections are quite full.  I'm using 2.1.1.  Any idea?

Thanks,
Davin

Mike Bostock

unread,
Aug 30, 2011, 1:33:52 PM8/30/11
to d3...@googlegroups.com
Are you using the "elastic" easing function? It's the only one where
ease(1) is slightly less than 1, which is probably a bug.

I can't think of any other reason why the transitions wouldn't finish,
short of another transition taking precedence. Have you tried
listening to the "end" event and seeing if they are actually ending?

Mike

Reply all
Reply to author
Forward
0 new messages