D3JS
D3JS
Timothy Bell
I3s - Université Côte d'Azur
timothy.bell@i3s.unice.fr
What is d3.js?
● A JavaScript library for creating documents that contain data.
● Made for web, manipulates CSS, HTML, SVG.
● Any modern browser, (personal computer, television, phone, etc. )
● It combines powerful visualization components and a data-driven approach to DOM
manipulation.
2
Overview of Web Standards
● ES6 (ECMAScript 6)
● Bundle.js
3
HTML (HyperText Markup Language)
4
CSS (Cascading Stylesheets)
● CSS is a language for styling HTML pages.
● CSS styles (also known as selectors) are typically applied to HTML tags based on their name, class,
or identifier.
parent node
child nodes
sibling node
6
JavaScript HTML DOM
● JavaScript is the programming language of HTML and the Web.
● DOM and JavaScript allow the creation of dynamic HTML:
○ changing/adding/removing HTML elements and its attributes;
○ changing the CSS style of elements and attributes;
○ react to existing HTML events in the page;
○ creating new events.
<!DOCTYPE html>
<html>
<head>
<title>TITLE GOES HERE</title>
</head>
<body>
<p id="demo"></p>
</body>
</html>
7
DOM + JavaScript D3 API
The standard DOM API is somewhat verbose, therefore libraries such as D3 provide syntactic sugar to ease
the manipulation of HTML elements, styles and attributes.
<ol>
D3.js
<li id=”some-id”>Unique element</li>
<li>Another list element</li> d3.select("#some-id")
<li> // [Array(1)]
<p>Paragraph inside list element</p>
<p>Second paragraph</p> d3.selectAll("p").size();
</li> // 4
</ol>
</body> var reds = d3.selectAll(".red")
</html> // [Array(1)]
reds.text()
// “Red Paragraph”
8
DOM + JavaScript D3 API
● The DOM also handles tracking elements as they are rendered, e.g. mouse movement.
○ Listeners may be attached to these events to add various levels of interactivity to the web page, e.g. mouseover, mouseleave
9
SVG (Scalable Vector Graphics)
HTML
● SVG is a XML format used for drawing. <!DOCTYPE html>
<html>
<head>
● Similar to DOM, SVG has elements with parents, children <title>TITLE GOES HERE</title>
</head>
and attributes. <body>
<svg width=”300” height=”180”>
<circle cx=”30” cy=”50” r=”25” />
○ The elements also respond to the same mouse/touch <circle cx=”90” cy=”50” r=”25” class=”red” />
<circle cx=”150” cy=”50” r=”25” class=”fancy” />
events.
<rect x=”10” y=”80” width=”40” height=”40” fill=”steelBlue” />
<rect x=”70” y=”80” width=”40” height=”40” style=”fill: steelBlue;” />
● SVG defines tags for lots of basic shapes: <rect x=”130” y=”80” width=”40” height=”40” class=”fancy” />
</svg>
○ <rect> for rectangles </body>
</html>
.fancy {
fill: none;
stroke: black;
stroke-width: 3pt;
stroke-dasharray: 3,5,10;
}
10
SVG - Groups
● Grouping elements:
○ DOM: <div> and <span> tags
11
SVG - Paths
The <path> tag
● Chart Elements
○ The Data
○ The Scales
○ The Axes
● Using SVG
● Using D3
○ Helpers
○ Defining the Scales
○ Defining the Axes
○ Binding the Data
13
Chart Elements
● The Scale: refers to the coordinate system
○ x-axis: from January 2014 to April 2014
○ y-axis: from $10 to $80
○ SVG dimensions: 350 by 160 pixels
■ Specify the mapping between data (i.e. values of variables) and pixels of the
screen
○ Note: y-axis flips as the SVG origin (i.e. the coordinates 0, 0) is in the top left
14
Using SVG
● Points and lines are drawn manually using SVG tags
○ The <path> tag is complex to be used by hand
15
<!DOCTYPE html>
<html>
<head>
Using D3
<title>TITLE GOES HERE</title>
</head>
<body>
<svg></svg>
</body>
</html>
var margin = {
2. Define the dimensions of the chart left: 20,
top: 10,
bottom: 20,
right: 10
}, // the margins of the chart
width = 350, // the width of the svg
height = 160; // the height of the svg
16
Using D3 – Helpers (selections)
● D3 selections are a group of elements that match a query or could match a query later (the
elements may not have been constructed yet).
● How to:
○ d3.select(<key>) will find one element
■ typically using the element’s unique identifier
○ d3.selectAll(<key>) will match all available elements
■ typically used to select elements with a same class or name
17
Using D3 – Helpers (min, max, extent)
● Automate operations such as finding the minimum and maximum values of a dataset (or both at the same time, the
“extent”).
● The data is always represented as Javascript array objects
var values = [10, 20, 40, 80],
data = [
{ date: “2014-01-01”, amount:10 },
{ date: “2014-02-01”, amount:20 },
{ date: “2014-03-01”, amount:40 },
{ date: “2014-04-01”, amount:80 }
]
d3.min(values)
// 10
● When working with JSON objects, we use callback functions to recover the values we want to use in the helper
method.
○ The callback function has normally two arguments: the element and its index.
○ These arguments are commonly named d and i, respectively.
d3.max(values)
// 80
d3.max(data, (d,i) => d.amount)
// 80
d3.extent(values)
// [10, 80]
d3.extent(data, (d,i) => d.amount)
// [10, 80]
18
Using D3 – Defining the Scales
● d3-scale module (https://observablehq.com/@d3/introduction-to-d3s-scales) : map values across coordinate systems
xScale(new Date("2014-02-01"))
// 124 // out: 124px
● Scales are not just for linear transforms (continuous or quantitative scales), but also for
arbitrary transforms (discrete or ordinal scales)
○ e.g., mapping between data and colors.
20
Using D3 – Defining the Axes
● We want labels and thick marks:
○ d3 can handle this automatically.
● When building an axis object, we give to it the scale we want to use as argument to the function.
chartGroup.append("g")
.attr('transform', "translate(0, 0)")
.classed('y-axis', true)
.call(yAxis)
chartGroup.append("g")
.attr('transform', "translate(0," + height + ")")
.classed('x-axis', true)
.call(xAxis)
21
Using D3 – Data Binding
Data binding might require the following operations:
● Selections:
○ var selection = d3.selectAll(<key>)
● Joining the data:
○ selection.data(<data-object>): for binding data to multiple elements
○ selection.datum(<data-object>): for binding data to a single element
● Creating elements
○ selection.enter()
○ selection.append(<element-name>): when data won’t change
○ selection.join() (v5+): when data update is required (i.e. filters)
● Transitions
○ selection.transition()
○ selection.duration(<time-milliseconds>)
● Tooltips
22
Using D3 – Data Binding
● We select the <g> element and bind our data using the data(<data-obj>) function:
var svg = d3.select("svg")
.attr('width', width + margin.left + margin.right) var circles = chartGroup.selectAll(“circle”)
.attr('height', height + margin.top + margin.bottom) .data(data);
Now we have a selection but still no elements! We have more work to do…
24
Using D3 – Data Binding (enter)
● Since this is the first data binding (there are no circles in the page yet), the process is straightforward.
○ Note: For the next selection, we must handle the fact that there will already be circles drawn on the page.
● We use selection.enter(), to indicate that we want to add new elements to the page.
25
Using D3 – Data Binding (append / join)
● Then, we we use selection.append(<key>) or selection.join(<key>) to add these new elements.
newCircles.join("circle") newCircles.append("circle")
.attr("cx", d => xScale(new Date(d.date))) .attr("cx", d => xScale(new Date(d.date)))
.attr("cy", d => yScale(d.amount)) .attr("cy", d => yScale(d.amount))
.attr("r", 5) .attr("r", 5)
.style("fill", "steelblue") .style("fill", "steelblue")
chartGroup.selectAll("circle") chartGroup.selectAll("circle")
.data(data) .data(data)
.join("circle") .enter()
.attr("cx", d => xScale(new Date(d.date))) .append("circle")
.attr("cy", d => yScale(d.amount)) .attr("cx", d => xScale(new Date(d.date)))
.attr("r", 5) .attr("cy", d => yScale(d.amount))
.style("fill", "steelblue") .attr("r", 5)
.style("fill", "steelblue")
26
Using D3 – Data Binding (datum)
● We create a line through a path created with all the points in the data.
27
Using D3 – Data Binding (data update)
1. If we have more data, we add new elements
2. If we have less data, we remove the elements that do not correspond to the new data we want to join in our chart
3. We update the elements attributes and style according to the new data
chartGroup.selectAll("circle")
var data = [ .data(data)
{ date: “2014-01-01”, amount:10 }, .join(
{ date: “2014-02-01”, amount:20 }, enter => enter.append(“circle”)
{ date: “2014-03-01”, amount:40 }, .attr("r", 5)
{ date: “2014-04-01”, amount:80 } .style("fill", "steelblue"),
] update => update,
exit => exit.remove()
)
.attr("cx", d => xScale(new Date(d.date)))
data.pop() .attr("cy", d => yScale(d.amount))
chartGroup.selectAll("path.line")
// [{ date: “2014-01-01”, amount:10 },
.attr("d", lineGenerator(data))
// { date: “2014-02-01”, amount:20 },
// { date: “2014-03-01”, amount:40 }]
28
Using D3 – Data Binding (Transitions)
● The operation selection.transition() allows temporal transitions to make transitions nicer.
● We can use attributes such as .duration(), .delay() and .ease(). We typically start by defining the duration to our
transition:
chartGroup.selectAll("circle")
.data(data)
.enter()
.join(
enter => enter.append(“circle”)
.attr("r", 5)
.style("fill", "steelblue"),
update => update,
exit => exit.remove()
)
.transition()
.duration(500)
.attr("cx", d => xScale(new Date(d.date)))
.attr("cy", d => yScale(d.amount))
chartGroup.selectAll("path.line")
.transition()
.duration(500)
.attr("d", lineGenerator(data))
29
Using D3 – Data Binding (Tooltips)
● We must often provide the user with more information than what is being visually represented, such as the actual values of the
represented variables
● We use then tooltips activated with mouseover and mouseout listeners that we add to our elements:
CSS
HTML
.tooltip{
<div class="tooltip"> </div> position: absolute;
z-index: 1000; /* to be in front of all other elements */
display: none; /* initially invisible */
background-color: #fff;
box-shadow: 10px 5px 5px #ccc;
border-radius: 5px;
}
D3.js
chartGroup.selectAll("circle")
.on("mouseover", function(d){
var x = d3.event.pageX,
y = d3.event.pageY;
d3.select("div.tooltip")
.style("display", "block")
.style("left", x + "px")
.style("top", y + "px")
.html("Date: " + d.date + "<br>" + "Amount: " + d.amount)
})
.on("mouseout", function(d){
d3.select("div.tooltip").style("display", "none")
})
31