+ - 0:00:00
Notes for current slide
Notes for next slide

D3 V4 - What's new?

Play along:

D3 v3 playground | D3 v4 playground

Slides: iros.github.io/d3-v4-whats-new/

bocoup

Irene Ros
@ireneros
irene@bocoup.com
1 / 103

Modularity

D3 v3 was one large library. You had to include all of it, even if you didn't need all of it.

2 / 103

Modularity

D3 v3 was one large library. You had to include all of it, even if you didn't need all of it.

D3 v4 is actually a set of small modules. You can choose to use one or more as you need them.

https://github.com/d3/

modules

3 / 103

Modularity

For example: https://github.com/d3/d3-scale Can be installed via npm:

npm install d3-scale

4 / 103

Modularity

For example: https://github.com/d3/d3-scale Can be installed via npm:

npm install d3-scale

Note that each module has it's own dependencies. When installing them via npm, they will be pulled down for you, but if you were to include them manually, you need to make sure those are included as well. For example:

<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-collection.v1.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-format.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-time.v1.min.js"></script>
<script src="https://d3js.org/d3-time-format.v2.min.js"></script>
<script src="https://d3js.org/d3-scale.v1.min.js"></script>

Most likely you will use some sort of build system to package your JS. Mike Bostock has suggested using Rollup an example of which you can find here.

5 / 103

Modularity

You can still use the entire v4 build by including it using your favorite method:

<script src="https://d3js.org/d3.v4.js"></script>
6 / 103

Namespacing

D3 v3's API was logically namespaced. For example:

  • d3.scale.linear
  • d3.scale.ordinal
  • d3.time.format
  • d3.svg.axis
7 / 103

Namespacing

D3 v3's API was logically namespaced. For example:

  • d3.scale.linear
  • d3.scale.ordinal
  • d3.time.format
  • d3.svg.axis

In D3 v4, all modules share a flat namespace d3.* as a result of adopting ES6 modules.

  • d3.scale.linear is now d3.scaleLinear
  • d3.layout.treemap is now d3.treemap
8 / 103

Namespacing

D3 v3's API was logically namespaced. For example:

  • d3.scale.linear
  • d3.scale.ordinal
  • d3.time.format
  • d3.svg.axis

In D3 v4, all modules share a flat namespace d3.* as a result of adopting ES6 modules.

  • d3.scale.linear is now d3.scaleLinear
  • d3.layout.treemap is now d3.treemap

This does mean you can't just swap in D3 v4 and expect it to work.

9 / 103

Method accessors

Many utility methods now take an accessor method that has a consistent signature:

function(d, i, data) {...}

For example:

var settlersData = [
{name: "Wood", count: 2},
{name: "Ore", count: 3},
{name: "Sheep", count: 4},
{name: "Wheat", count: 1}
];
d3.min(settlersData, function(d, i, data) {
return d.count;
});

Previously some methods were not consistent about this (for example, d3.quantile).

10 / 103

Canvas Rendering

In v3, d3's few actual rendering methods only rendered to SVG. Starting v4, many of the rendering helpers now have a new context method that lets you pass in a Canvas context to render to.

For example:

var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
11 / 103

Canvas Rendering

In v3, d3's few actual rendering methods only rendered to SVG. Starting v4, many of the rendering helpers now have a new context method that lets you pass in a Canvas context to render to.

For example:

var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });

To render SVG:

path.datum(data).attr("d", line);
12 / 103

Canvas Rendering

In v3, d3's few actual rendering methods only rendered to SVG. Starting v4, many of the rendering helpers now have a new context method that lets you pass in a Canvas context to render to.

For example:

var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });

To render SVG:

path.datum(data).attr("d", line);

To render to Canvas:

line.context(context)(data);

You can see more examples in the shapes module: d3-shape.

13 / 103

Selections

  • Selections no longer subclass Array by extending its prototype. They are now plain objects.
14 / 103

Selections

  • Selections no longer subclass Array by extending its prototype. They are now plain objects.

  • There is a new method called .nodes to retrieve all nodes within a selection as an array.

15 / 103

Selections

  • Selections no longer subclass Array by extending its prototype. They are now plain objects.

  • There is a new method called .nodes to retrieve all nodes within a selection as an array.

  • Selections are immutable (parents and elements never change, though their attributes do.) For example. selection.sort will return a new selection.

16 / 103

Selections

  • selection.append now inserts elements in the order of the data bound, instead of at the end:

For example, in v3:

d3.select("body").selectAll("span").data([1, 3, 5], String).enter().append("span").text(String);
d3.select("body").selectAll("span").data([1, 2, 3, 4, 5], String).enter().append("span").text(String);
// 13524

In v4:

d3.select("body").selectAll("span").data([1, 3, 5], String).enter().append("span").text(String);
d3.select("body").selectAll("span").data([1, 2, 3, 4, 5], String).enter().append("span").text(String);
// 12345
17 / 103

Selections

  • selection.append now inserts elements in the order of the data bound, instead of at the end:

For example, in v3:

d3.select("body").selectAll("span").data([1, 3, 5], String).enter().append("span").text(String);
d3.select("body").selectAll("span").data([1, 2, 3, 4, 5], String).enter().append("span").text(String);
// 13524

In v4:

d3.select("body").selectAll("span").data([1, 3, 5], String).enter().append("span").text(String);
d3.select("body").selectAll("span").data([1, 2, 3, 4, 5], String).enter().append("span").text(String);
// 12345
  • New lifecycle event (ish)! merge.
18 / 103

example

In v3:

var circleBinding = svg.selectAll("circle").data(data);
circleBinding.style("fill", "blue"); // UPDATE
circleBinding.exit().remove(); // EXIT
circleBinding.enter().append("circle") // ENTER; modifies UPDATE!
.style("fill", "green");
circleBinding // ENTER + UPDATE
.style("stroke", "black");
19 / 103

example

In v3:

var circleBinding = svg.selectAll("circle").data(data);
circleBinding.style("fill", "blue"); // UPDATE
circleBinding.exit().remove(); // EXIT
circleBinding.enter().append("circle") // ENTER; modifies UPDATE!
.style("fill", "green");
circleBinding // ENTER + UPDATE
.style("stroke", "black");

In v4:

var circleBinding = svg.selectAll("circle").data(data);
circleBinding.style("fill", "blue"); // UPDATE
circleBinding.exit().remove(); // EXIT
circleBinding.enter().append("circle") // ENTER
.style("fill", "green")
.merge(circleBinding) // ENTER + UPDATE
.style("stroke", "black");
20 / 103

Selections (d3-selection)

  • To move a selection to the front of its siblings call selection.raise. To move it backwards, call selection.lower
21 / 103

Selections (d3-selection)

  • To move a selection to the front of its siblings call selection.raise. To move it backwards, call selection.lower

  • You can dispatch events on selections using selection.dispatch

22 / 103

Selections (d3-selection)

  • To move a selection to the front of its siblings call selection.raise. To move it backwards, call selection.lower

  • You can dispatch events on selections using selection.dispatch

  • The selection.call method no longer sets this to be the selection. It is now passed as the first argument to the callback.

In v3:

d3.selectAll("div.states")
.call(function() {
this.style('color', 'red');
})

In V4:

d3.selectAll("div.states")
.call(function(statesSelection) {
statesSelection.style('color', 'red');
})
23 / 103

Multi-Selections (d3-selection-multi)

In d3 v3, you could easily set multiple style or attribute properties like this:

d3.select("h4").style({
color: "red", background: "yellow"
});
24 / 103

Multi-Selections (d3-selection-multi)

In d3 v3, you could easily set multiple style or attribute properties like this:

d3.select("h4").style({
color: "red", background: "yellow"
});

In v4, you can no longer do that. Even this does not work:

d3.select("h4").style({ color: "red" });

Instead, you have to set the key and value explicitly:

d3.select("h4").style("color", "red");
25 / 103

Multi-Selections (d3-selection-multi)

Multi-selections allows you to set multiple properties with one call, but it is not included by default with the standard build. You have to include it yourself:

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>

This will let you:

var div = d3.selectAll("div")
.attrs({ "title": "Hello, world!", "name" : "greeter" })
.styles({ "color": "red", "background" : "green" });

Note that it's styles and attrs, not style and attr as before.

26 / 103

Transitions (d3-transition)

You can create transitions and then share them across selections

var t = d3.transition()
.duration(750)
.ease(d3.easeLinear);
d3.selectAll(".apple").transition(t)
.style("fill", "red");
d3.selectAll(".orange").transition(t)
.style("fill", "orange");
27 / 103

Transitions (d3-transition)

Delays are now relative to previous transition, not the first transition

d3.selectAll(".apple")
.transition() // First fade to green.
.style("fill", "green")
.transition() // Then red.
.style("fill", "red")
.transition() // Wait one second. Then brown, and remove.
.delay(1000)
.style("fill", "brown")
.remove();
28 / 103

Transitions (d3-transition)

Delays are now relative to previous transition, not the first transition

d3.selectAll(".apple")
.transition() // First fade to green.
.style("fill", "green")
.transition() // Then red.
.style("fill", "red")
.transition() // Wait one second. Then brown, and remove.
.delay(1000)
.style("fill", "brown")
.remove();
  • selection.interrupt stops active transitions and cancels all scheduled transitions on the selection.
29 / 103

Transitions (d3-transition)

Delays are now relative to previous transition, not the first transition

d3.selectAll(".apple")
.transition() // First fade to green.
.style("fill", "green")
.transition() // Then red.
.style("fill", "red")
.transition() // Wait one second. Then brown, and remove.
.delay(1000)
.style("fill", "brown")
.remove();
  • selection.interrupt stops active transitions and cancels all scheduled transitions on the selection.

  • All transition callbacks recieve the standard arguments (d, i, nodes) - which impacts in particular the attrTween and styleTween functions which took a tween function or style value as the third argument.

30 / 103
  • d3.active lets you select a transition that is currently active on a given node.

Example

var whiteblue = d3.interpolateRgb("#eee", "steelblue"),
blueorange = d3.interpolateRgb("steelblue", "orange"),
orangewhite = d3.interpolateRgb("orange", "#eee");
d3.select("body").selectAll("div")
.data(d3.range(n))
.enter().append("div")
.transition()
.delay(function(d, i) { return i + Math.random() * n / 4; })
.ease(d3.easeLinear)
.on("start", function repeat() {
d3.active(this)
.styleTween("background-color", function() { return whiteblue; })
.transition()
.delay(1000)
.styleTween("background-color", function() { return blueorange; })
.transition()
.delay(1000)
.styleTween("background-color", function() { return orangewhite; })
.transition()
.delay(n)
.on("start", repeat);
});
31 / 103

Scales (d3-scale)

  • Lots of renaming:

    • d3.scale.lineard3.scaleLinear
    • d3.scale.sqrtd3.scaleSqrt
    • d3.scale.powd3.scalePow
32 / 103

Scales (d3-scale)

  • Lots of renaming:

    • d3.scale.lineard3.scaleLinear
    • d3.scale.sqrtd3.scaleSqrt
    • d3.scale.powd3.scalePow
  • Scales now generate ticks in the same order as the domain (useful if you have a descending scale)

33 / 103

Scales (d3-scale)

  • Lots of renaming:

    • d3.scale.lineard3.scaleLinear
    • d3.scale.sqrtd3.scaleSqrt
    • d3.scale.powd3.scalePow
  • Scales now generate ticks in the same order as the domain (useful if you have a descending scale)

  • You can specify what to do when a scale recieves a value that is outside the domain using .unknown:

var x = d3.scaleOrdinal()
.domain([0, 1])
.range(["red", "blue"])
.unknown(undefined);
x(0) // "red"
x(2); // undefined
34 / 103

Scales (d3-scale)

  • Categorical color scales have changed. They have been renamed: d3.scale.category10d3.schemeCategory10 and they are no longer actual scales but just array of numbers:

In v3:

var color = d3.scale.category10();

In V4:

var color = d3.scaleOrdinal(d3.schemeCategory10);
35 / 103

Scales (d3-scale)

  • Categorical color scales have changed. They have been renamed: d3.scale.category10d3.schemeCategory10 and they are no longer actual scales but just array of numbers:

In v3:

var color = d3.scale.category10();

In V4:

var color = d3.scaleOrdinal(d3.schemeCategory10);
  • New sequential scales - instead of outputting to a range, they use an interpolator. Most useful for sequential color schemes. Lots of new interplators inspired by matplotlib's perceptually motivated colormaps.
z = d3.scaleSequential(d3.interpolateViridis);

interpolateViridis

36 / 103

Histograms (d3-array)

d3.histogram

In v4:

var values = [
{ val : 1 }, { val : 1 }, { val : 4 }, { val : 5 }, { val : 23 },
{ val : 1 }, { val : 4 }
];
var hist = d3.histogram()
.thresholds(3)
.value(function(d) {
return d.val;
});
37 / 103

Histograms (d3-array)

d3.histogram

  • d3.layouts.histogram is now d3.histogram
38 / 103

Histograms (d3-array)

d3.histogram

  • d3.layouts.histogram is now d3.histogram

  • The bins method was renamed to thresholds

39 / 103

Histograms (d3-array)

d3.histogram

  • d3.layouts.histogram is now d3.histogram

  • The bins method was renamed to thresholds

  • dx which specified the bin width is gone, there are now x0 and x1 which specify the start and end range of a bin. x0 will equal the x1 of the previous bin.

40 / 103

Histograms (d3-array)

d3.histogram

  • d3.layouts.histogram is now d3.histogram

  • The bins method was renamed to thresholds

  • dx which specified the bin width is gone, there are now x0 and x1 which specify the start and end range of a bin. x0 will equal the x1 of the previous bin.

The binning is also now different. In v3 the values we have for x and dx are:

[1, 7.33333] [8.33333333, 7.33333], [15.666666666666666, 7.33333]

In V4 the bins are:

[1, 10] [10, 20] [20, 23]

41 / 103

Axes (d3-axis)

In v3, you had to style the axes or it would look like this:

v3 axes

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font: 10px sans-serif;
}
d3.select(".axis")
.call(d3.svg.axis()
.scale(x)
.orient("bottom"));
42 / 103

Axes (d3-axis)

In v4, styling is taken care of:

var x = d3.scaleLinear()
.domain([1,10])
.range([1,300]);
var axis = d3.axisTop(x);
d3.select("body").append("svg")
.attr("class", "axis")
.attr("width", 310)
.attr("height", 30)
.append("g")
.attr("transform", "translate(0,20)")
.call(axis);

Results in:

v4 axes

The default styles have been changed to offset the axis by 0.5px which fixes a crisp-edges rendering issue on safari where the axis would be drawn 2px thick.

43 / 103

Axes (d3-axis)

  • Note that instead of setting the axis orientation we now use
    • axisLeft
    • axisRight
    • axisTop
    • axisBottom
44 / 103

Axes (d3-axis)

  • Note that instead of setting the axis orientation we now use
    • axisLeft
    • axisRight
    • axisTop
    • axisBottom

axis.tickArguments([20, "s"]) - shorthand for calling axis.ticks(20) and axis.tickFormat(d3.format("s")).

45 / 103

Collections (d3-collections)

Aggregates all functions having to do with data structures

  • Objects
  • Maps
  • Sets
  • Nests
46 / 103

Collections (d3-collections)

d3.set

  • Renamed: set.forEach to set.each
  • Now accepts an accessor function
var values = [
{ val : 1 },
{ val : 1 },
{ val : 4 },
{ val : 5 },
{ val : 23 },
{ val : 1 },
{ val : 4 }
];
var x = d3.set(values, function(d) {
return d.val;
}); // [1,4,5,23]
47 / 103

Collections (d3-collections)

d3.map

  • Renamed: map.forEachmap.each.
  • Argument order changed for each callback to (value, key, map):
var values = [
{ val : 1, name: "One"},
{ val : 1, name: "One" },
{ val : 4, name: "Four" },
{ val : 5, name: "Five" },
{ val : 23, name: "TwentyThree" },
{ val : 1, name: "One" }
];
var x = d3.map(values, function(d) { return d.name; });
x.each(function(val, key, map) {
console.log(val);
}); // { val : 1, name: "One"}, { val : 4, name: "Four" }, { val : 5, name: "Five" },...

In V3, the order was (key, value). So if you would get: ["One", "Four", "Five", "TwentyThree"] if you forget to reverse your callback variables...

48 / 103

Collections (d3-collections)

Both d3.set and d3.map

  • Return the collection, when you call .add to add a new element, instead of the added value.
  • Have new .clear methods to clear the collections.
49 / 103

Arrays (d3-array)

d3.scan(arr, fn)

D3 v3's d3.min and d3.max would return the min or max element appropriately, but you couldn't easily find out the original position of that element. In v4, d3.scan does just that using a comparator function.

var settlersData = [
{name: "Wood", count: 2},
{name: "Ore", count: 3},
{name: "Sheep", count: 4},
{name: "Wheat", count: 1}
];
var idx = d3.scan(settlersData, function(a, b) {
return a.count - b.count;
}); // 3
settlersData[idx]; // {name: "Wheat", count: 1}
50 / 103

Arrays (d3-array)

d3.ticks(start, end, count)

A new method that will generate an array of count+1 items equally spaced between start and end.

var ticks = d3.ticks(0, 10, 5); // [0, 2, 4, 6, 8, 10]

Note that this method has nothing to do with axes.

d3.tickStep(start, end, count)

Will give you the space between values that you would obtain from d3.ticks:

d3.tickStep(0,10,5);
// 2
51 / 103

Arrays (d3-array)

d3.range(start, end, increment)

Floating point math is fun:

0.2 * 3 = 0.6000000000000001

In D3 v3, d3.range tried to handle this:

d3.range(0, 1, 0.2) // [0, 0.2, 0.4, 0.6, 0.8]

D3 v4's d3.range no longer does that:

d3.range(0, 1, 0.2) // [0, 0.2, 0.4, 0.6000000000000001, 0.8]

This only matters if you are dealing with floating point numbers and you can always use d3.format to present them more consistently.

52 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.
53 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.

  • You can also pass an optional opacity argument to d3.rgb, d3.hsl, d3.lab, d3.hcl or d3.cubehelix.

54 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.

  • You can also pass an optional opacity argument to d3.rgb, d3.hsl, d3.lab, d3.hcl or d3.cubehelix.

  • You can now parse CSS color specifiers with d3.color:

var red = d3.color("hsl(0, 80%, 50%)");
// {h: 0, l: 0.5, s: 0.8, opacity: 1}
55 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.

  • You can also pass an optional opacity argument to d3.rgb, d3.hsl, d3.lab, d3.hcl or d3.cubehelix.

  • You can now parse CSS color specifiers with d3.color:

var red = d3.color("hsl(0, 80%, 50%)");
// {h: 0, l: 0.5, s: 0.8, opacity: 1}
  • The toString method will now return an rgb or rgba string in the form of rgb(12,13,42).
56 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.

  • You can also pass an optional opacity argument to d3.rgb, d3.hsl, d3.lab, d3.hcl or d3.cubehelix.

  • You can now parse CSS color specifiers with d3.color:

var red = d3.color("hsl(0, 80%, 50%)");
// {h: 0, l: 0.5, s: 0.8, opacity: 1}
  • The toString method will now return an rgb or rgba string in the form of rgb(12,13,42).

  • There is no more .hsl method on a color instance. To convert a color to hsl call the d3.hsl helper method:

d3.hsl(red)
// Color instance: {h: 0, s: 0.8, l: 0.5, opacity: 1}
57 / 103

Colors (d3-color)

  • All colors now have an opacity method exposed: .opacity which takes a value from 0 to 1.

  • You can also pass an optional opacity argument to d3.rgb, d3.hsl, d3.lab, d3.hcl or d3.cubehelix.

  • You can now parse CSS color specifiers with d3.color:

var red = d3.color("hsl(0, 80%, 50%)");
// {h: 0, l: 0.5, s: 0.8, opacity: 1}
  • The toString method will now return an rgb or rgba string in the form of rgb(12,13,42).

  • There is no more .hsl method on a color instance. To convert a color to hsl call the d3.hsl helper method:

d3.hsl(red)
// Color instance: {h: 0, s: 0.8, l: 0.5, opacity: 1}
  • New method called color.displayable() will return false if the color is out of gamut.
58 / 103

Easing (d3-ease)

In v3, you used easing function string names to specify the ones you wanted like "linear" or "cubic-in-out". In V4, we use methods instead:

Instead of:

var e = d3.ease("elastic-out-in", 1.2);

Use:

var e = d3.easeElastic.amplitude(1.2);

Visual reference for the functions

Animated version

59 / 103

Dispatching (d3-dispatch)

  • Instead of exposing individual methods for each event type, you just use call to fire off an event (note these are different from the selection.call).

In V3:

dispatcher.foo.call(that, "Hello, Foo!");

In V4:

dispatcher.call("foo", that, "Hello, Foo!");
60 / 103

Dispatching (d3-dispatch)

  • Instead of exposing individual methods for each event type, you just use call to fire off an event (note these are different from the selection.call).

In V3:

dispatcher.foo.call(that, "Hello, Foo!");

In V4:

dispatcher.call("foo", that, "Hello, Foo!");
  • The on method accepts multiple event names, separated by a space:
dispatcher.on("foo bar", function(message) {
console.log(message);
});
61 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush
62 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

63 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

  • Brush is no longer depending on scales - they define a selection in screen coordinates and you can invert it using a scale of choice.

64 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

  • Brush is no longer depending on scales - they define a selection in screen coordinates and you can invert it using a scale of choice.

  • You can restrict a brushing area to a brush.extent, otherwise it defaults to the full SVG element.

65 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

  • Brush is no longer depending on scales - they define a selection in screen coordinates and you can invert it using a scale of choice.

  • You can restrict a brushing area to a brush.extent, otherwise it defaults to the full SVG element.

  • A brush doesn't store the active selection - it is now stored on the element to which the brush was applied. You can access it either via:

    • a brush event - retrieve element by calling event.selection.
    • Call d3.brushSelection on any given element.
66 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

  • Brush is no longer depending on scales - they define a selection in screen coordinates and you can invert it using a scale of choice.

  • You can restrict a brushing area to a brush.extent, otherwise it defaults to the full SVG element.

  • A brush doesn't store the active selection - it is now stored on the element to which the brush was applied. You can access it either via:

    • a brush event - retrieve element by calling event.selection.
    • Call d3.brushSelection on any given element.
  • You can move the brush by calling brush.move

67 / 103

Brushing (d3-brush)

  • d3.svg.brush is now d3.brush

  • There are now 3 classes for brushing along the x, y or both dimensions: d3.brushX, d3.brushY or d3.brush.

  • Brush is no longer depending on scales - they define a selection in screen coordinates and you can invert it using a scale of choice.

  • You can restrict a brushing area to a brush.extent, otherwise it defaults to the full SVG element.

  • A brush doesn't store the active selection - it is now stored on the element to which the brush was applied. You can access it either via:

    • a brush event - retrieve element by calling event.selection.
    • Call d3.brushSelection on any given element.
  • You can move the brush by calling brush.move

  • There are now default styling applied, so you don't have to add them manually.

68 / 103

Brushing (d3-brush)

d3.brush

var brush = d3.brush()
.on("start brush", brushed)
.on("end", brushended);
function brushed() {
var s = d3.event.selection;
//...
}
function brushended() {
if (!d3.event.selection) {
//...
}
}
svg.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, [[307, 167], [611, 539]]);

Example: http://bl.ocks.org/mbostock/0d20834e3d5a46138752f86b9b79727e

69 / 103

Dragging (d3-drag)

  • d3.behavior.dragd3.drag

  • There's a new method called subject which lets you define an accessor to return what is being dragged. This is useful when using canvas, since there are no DOM nodes representing the content. The result of this accessor function call will be available on a drag event under d3.event.subject. Example: http://bl.ocks.org/mbostock/444757cc9f0fde320a5f469cd36860f4

70 / 103

Dragging (d3-drag)

  • d3.behavior.dragd3.drag

  • There's a new method called subject which lets you define an accessor to return what is being dragged. This is useful when using canvas, since there are no DOM nodes representing the content. The result of this accessor function call will be available on a drag event under d3.event.subject. Example: http://bl.ocks.org/mbostock/444757cc9f0fde320a5f469cd36860f4

  • There is now an on method to bind to start, end and drag events:

canvas.call(d3.drag()
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
.on("start.render drag.render end.render", render));
71 / 103

Dragging (d3-drag)

  • d3.behavior.dragd3.drag

  • There's a new method called subject which lets you define an accessor to return what is being dragged. This is useful when using canvas, since there are no DOM nodes representing the content. The result of this accessor function call will be available on a drag event under d3.event.subject. Example: http://bl.ocks.org/mbostock/444757cc9f0fde320a5f469cd36860f4

  • There is now an on method to bind to start, end and drag events:

canvas.call(d3.drag()
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
.on("start.render drag.render end.render", render));
  • There are also d3.dragEnable and d3.dragDisable methods to control dragging support at a higher level.
72 / 103

Force layout (d3-force)

  • Renamed: d3.layout.forced3.forceSimulation.
73 / 103

Force layout (d3-force)

  • Renamed: d3.layout.forced3.forceSimulation.

  • The force algorithm changed to track node velocity (node.vx and node.vy) and position (node.x and node.y, not just position (formally node.px and node.py).

74 / 103

Force layout (d3-force)

  • Renamed: d3.layout.forced3.forceSimulation.

  • The force algorithm changed to track node velocity (node.vx and node.vy) and position (node.x and node.y, not just position (formally node.px and node.py).

  • Initial node position is now deterministic

75 / 103

Force layout (d3-force)

  • Renamed: d3.layout.forced3.forceSimulation.

  • The force algorithm changed to track node velocity (node.vx and node.vy) and position (node.x and node.y, not just position (formally node.px and node.py).

  • Initial node position is now deterministic

  • If you need to find the closest node to a pointer, you no longer have to use a Voronoi SVG overlay, you can call simulation.find.

76 / 103

Force layout (d3-force)

  • Renamed: d3.layout.forced3.forceSimulation.

  • The force algorithm changed to track node velocity (node.vx and node.vy) and position (node.x and node.y, not just position (formally node.px and node.py).

  • Initial node position is now deterministic

  • If you need to find the closest node to a pointer, you no longer have to use a Voronoi SVG overlay, you can call simulation.find.

  • Old v3 version of Les Miserables. In v4: Les Miserables with dragging

var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
77 / 103

Hierarchies (d3-hierarchy)

  • Lots of renaming:

    • d3.layout.clusterd3.cluster
    • d3.layout.hierarchyd3.hierarchy
  • New useful method d3.stratify to convert flat arrays of objects into the hierarchical structures required by hierarchical layouts:

[
{"name": "Eve", "parent": ""},
{"name": "Cain", "parent": "Eve"},
{"name": "Seth", "parent": "Eve"},
{"name": "Enos", "parent": "Seth"}
]
78 / 103

To convert to a hierarchy:

var root = d3.stratify()
.id(function(d) { return d.name; })
.parentId(function(d) { return d.parent; })
(table);

image

79 / 103

Hierarchies (d3-hierarchy)

  • All hierarchical layouts now expect a hierarchy object. So if you already have json in the right form you first have to call var struct = d3.hierarchy(myJsonObj) to convert it and then pass it to your layout.
80 / 103

Hierarchies (d3-hierarchy)

  • All hierarchical layouts now expect a hierarchy object. So if you already have json in the right form you first have to call var struct = d3.hierarchy(myJsonObj) to convert it and then pass it to your layout.

  • There are new methods for aggregating special nodes like:

    • nodes.leaves to get just leaf nodes
    • nodes.descendents
    • nodes.anscestors
    • To get the links in the form of {source, target} call nodes.links (which you used to obtain from your layouts.)
81 / 103

Queue (d3-queue)

  • A queue evaluates zero or more deferred asynchronous tasks with configurable concurrency
  • Used to be a separate library, now included in the default bundle as d3.queue.
d3.queue()
.defer(d3.json, 'states.json')
.defer(d3.json, 'cities.json')
.await(function(error, states, cities) {
// make map
});
// or:
d3.queue()
.defer(d3.json, 'states.json')
.defer(d3.json, 'cities.json')
.awaitAll(function(error, results) {
// results is an array of the returned objects.
});
82 / 103

Delimiter-Separated Values (d3-dsv)

  • Lots of renaming:

    • d3.csv.parsed3.csvParse
    • d3.csv.parseRowsd3.csvParseRows
83 / 103

Delimiter-Separated Values (d3-dsv)

  • Lots of renaming:

    • d3.csv.parsed3.csvParse
    • d3.csv.parseRowsd3.csvParseRows
  • The original d3.csv and d3.tsv for loading files have not been renamed.

84 / 103

Delimiter-Separated Values (d3-dsv)

  • Lots of renaming:

    • d3.csv.parsed3.csvParse
    • d3.csv.parseRowsd3.csvParseRows
  • The original d3.csv and d3.tsv for loading files have not been renamed.

  • There is no more d3.dsv for custom formatters, you can now use d3.dsvFormat("|") to define a new formatter and parser.

85 / 103

Delimiter-Separated Values (d3-dsv)

  • Lots of renaming:

    • d3.csv.parsed3.csvParse
    • d3.csv.parseRowsd3.csvParseRows
  • The original d3.csv and d3.tsv for loading files have not been renamed.

  • There is no more d3.dsv for custom formatters, you can now use d3.dsvFormat("|") to define a new formatter and parser.

  • The parse method now exposes column names:

d3.csv("cars.csv", function(error, data) {
if (error) throw error;
console.log(data.columns); // ["Year", "Make", "Model", "Length"]
});
86 / 103

Polygons (d3-polygon)

  • d3.geom.polygon is gone and there is no d3.polygon.

  • Now using d3.polygonHull(points) which returns the convex hull of the specified points. The returned hull is represented as an array containing a subset of the input points arranged in counterclockwise order.

87 / 103

Polygons (d3-polygon)

  • d3.geom.polygon is gone and there is no d3.polygon.

  • Now using d3.polygonHull(points) which returns the convex hull of the specified points. The returned hull is represented as an array containing a subset of the input points arranged in counterclockwise order.

Some helper methods to call with the results of polygonHull:

  • d3.polygonArea - area of polygon
  • d3.polygonCentroid - center point of polygon
  • d3.polygonContains - checks if a point is inside of a polygon.
  • d3.polygonLength - returns the length of the perimiter.

Example here

88 / 103

Quadtree (d3-quadtree)

A quadtree recursively partitions two-dimensional space into squares, dividing each square into four equally-sized squares. http://bl.ocks.org/mbostock/9078690

  • d3.geom.quadtreed3.quadtree
  • Quadtree is no longer a generator. Instead of defining a quadtree function that you pass your data to, you add your data to the quadtree you're generating using the add or addAll methods:

In v3:

var quadtree = d3.geom.quadtree()
.extent([[0, 0], [width, height]]);
quadtree(data);

In V4:

var quadtree = d3.quadtree()
.extent([[0, 0], [width, height]])
.addAll(data);
89 / 103

Quadtree (d3-quadtree)

  • Previously the quadtree generator returned the root node, now you can get it by calling quadtree.root.
90 / 103

Quadtree (d3-quadtree)

  • Previously the quadtree generator returned the root node, now you can get it by calling quadtree.root.

  • The leaf boolean flag on nodes is gone, now you can use node.length to determine if the node has children instead.

91 / 103

Quadtree (d3-quadtree)

  • Previously the quadtree generator returned the root node, now you can get it by calling quadtree.root.

  • The leaf boolean flag on nodes is gone, now you can use node.length to determine if the node has children instead.

  • quadtree.find now takes an optional radius (in screen px) that its search will be restricted to - useful when your quadtree has a lot of points.

92 / 103

Quadtree (d3-quadtree)

  • Previously the quadtree generator returned the root node, now you can get it by calling quadtree.root.

  • The leaf boolean flag on nodes is gone, now you can use node.length to determine if the node has children instead.

  • quadtree.find now takes an optional radius (in screen px) that its search will be restricted to - useful when your quadtree has a lot of points.

  • Implementation no longer recursive and much faster.

93 / 103

Other small but notable

  • Written in strict mode
94 / 103

Other small but notable

95 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)
96 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)

  • Uses only ASCII variable names and ASCII string literals. No more requirement to be explicit about UTF-8.

97 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)

  • Uses only ASCII variable names and ASCII string literals. No more requirement to be explicit about UTF-8.

  • d3.format now has text formatting: d3.format(">10c")("foo"); // " foo"

98 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)

  • Uses only ASCII variable names and ASCII string literals. No more requirement to be explicit about UTF-8.

  • d3.format now has text formatting: d3.format(">10c")("foo"); // " foo"

  • You can set a default locale based on country for some of your number parsing with d3.formatDefaultLocale link

99 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)

  • Uses only ASCII variable names and ASCII string literals. No more requirement to be explicit about UTF-8.

  • d3.format now has text formatting: d3.format(">10c")("foo"); // " foo"

  • You can set a default locale based on country for some of your number parsing with d3.formatDefaultLocale link

  • New random number generators for exponential and uniform distributions.

100 / 103

Other small but notable

  • Written in strict mode

  • d3-geo introduces fitSize, the greatest thing since sliced bread

  • The default build is anonymous (aka no global d3 object is exported if AMD or CommonJS is detected.)

  • Uses only ASCII variable names and ASCII string literals. No more requirement to be explicit about UTF-8.

  • d3.format now has text formatting: d3.format(">10c")("foo"); // " foo"

  • You can set a default locale based on country for some of your number parsing with d3.formatDefaultLocale link

  • New random number generators for exponential and uniform distributions.

  • d3.xhr is now d3.request, which also supports basic auth by calling .user and .password methods.

101 / 103

Not covered:

102 / 103

Thanks!

Irene Ros
@ireneros
irene@bocoup.com


103 / 103

Modularity

D3 v3 was one large library. You had to include all of it, even if you didn't need all of it.

2 / 103
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow