Technology / Tutorials

How D3.js Masters the DOM to Visualize Data for the Web

5 Feb 2016 4:46pm, by

This piece is the first part of a semi-sporadic series to explain the basics of how the D3.js JavaScript visualization library works.

d3

For a visualization tool, the D3.js JavaScript library is a strange beast. It is not a charting package per se, but a set of programming tools to bind data to the Document Object Model (DOM), the interface for representing HTML for browsers and Web applications. For instance, it can create an HTML table from an array of numbers.

If you just need a quick fix for putting up a bar chart on the Web, there are probably other JavaScript libraries better suited. But if you want to really want to be fluid with the visualization on the Web, D3 is your best bet in the long run.

“Here’s the deal. Instead of telling D3 how to do something, tell D3 what you want,” D3.js creator Mike Bostock, has stated, in a somewhat cryptic statement, at least for newbies.

D3 can be a bit daunting to learn, not the least because most tutorials and books dive right in, seemingly out of nowhere, of how the DOM operates. The DOM provides the browser API for HTML, giving the computer a standard way to represent, store and manipulate a document. It is not a programming language per se; it is a model.

D3 relies on a set of other Web standards as well. It is written in JavaScript, uses SVG for the graphics, CSS for display and layout, and DOM to embed the visualizations with the Web page.

In other words, D3 throws a lot at you. This set of tutorials, of which this is the first installment, will break it down into more bite-sized chunks, using code snippets from Bostick and other sources.

Beyond the DOM

Of course, if you wanted to create graphical elements you could just program the DOM directly. Bostick provides an example of how to add a div to the body element

<script>
var div = document.createElement("div");
div.innerHTML = "Hello, world!";
document.body.appendChild(div);
</script>

..which the browser renders as:

<div>Hello, world!</div>

Viewing the source code directly from the browser doesn’t show the div on the page, by the way, just the D3 code; instead, you have to open up the developer tools package within your favorite browser to view the div element:

elements

So that is pretty cool. But it doesn’t scale well for the programmer. Say you want to use a bunch of Divs to create a bar chart (a Web design no-no, but stay with us here). Using the DOM, it would be messy for the programmer as it requires the programmer to write out iterations and have the program keep track of state, as this example from the D3js.org site shows. here is the proverbial bar-chart rendered with the DOM directly:

<div class="chart">
 <div style="width: 40px;">4</div>
 <div style="width: 80px;">8</div>
 <div style="width: 150px;">15</div>
 <div style="width: 160px;">16</div>
 <div style="width: 230px;">23</div>
 <div style="width: 420px;">42</div>
</div>

Generally, Web developers loathe working with the DOM directly for exactly this reason. Instead, D3 takes a declarative approach. Instead writing each bar out by hand, the developer can write out a general formula for creating the bars. The same bar chart that could be rendered with the above code could be done thusly within D3.js:

d3.select(".chart")
 .selectAll("div")
 .data(data)
 .enter().append("div")
 .style("width", function(d) { return d * 10 + "px"; })
 .text(function(d) { return d; });

In an accompanying CSS block (not shown), the developer would specify how the bars should be created. In both the DOM and the D3 approach, the same bar chart is produced:

BarChart

Hello World

So how does D3 work? To render a “hello world” inside the div tag within D3 would require the following as coded by Bostick:

var body = d3.select("body");
var div = body.append("div");
div.html("Hello, world!");

That code goes into a set of script tags.  And, of course, you need a pointer to a copy of the library—either your own copy or on of the D3 site—somewhere in the head of the document:

<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

In the above code, you are appending the div tag and then putting some text in there. See, The beauty of using D3 is that it allows you to specify CSS and HTML constants, such as div or style, as functions. Containers, attribute values, class and ID names of HTML tags are also fair game for D3 manipulations.

Making use of the W3C Selectors API recommendation, D3 allows you specify groups of elements as a selection.

“Working with elements en masse gives selections their power; you can manipulate a single element or many of them without substantially restructuring your code. Although this may seem like a small change, eliminating loops and other control flow can make your code much cleaner,” wrote Bostock in a tutorial.

Key to this is the D3 select tag, which applies to the first element of the page. There is also the selectAll tag, which applies to all elements of the page. The code below will give you three copies of “Hello World!”:

<Addition></Addition>
<Addition></Addition>
<Addition></Addition>
<script>
var body = d3.selectAll("Addition");
var div = body.append("div");
div.html("Hello, world!");
</script>

Chain, Chain, Chain

Next, you got to learn how to read D3. D3 uses a syntax of method chaining, which JavaScript people may know from jQuery. The chain syntax allows you to link multiple methods together in a sequence, using the period (“.”) to tether them together.

With syntax chaining, the above script code can be simplified as:

<script>
d3.selectAll("Addition").append("div").text("Hello, world!");
</script>

So, .select, or .selectAll, selects the elements, .append adds the HTML tags, and .text inserts the text.

Bring the Data

So we know we can manipulate the elements through D3’s control of the DOM. The next step is to bind the data—the data you want visualized–to the elements. Data in, visuals out, yes?

We do this through the selection.data call.  Data can be an array of pretty much anything, including numbers, strings, or objects other arrays or key/value pairs, wrenched from a database or some other source using your favorite tools for the task. Simply declare the dataset array as a variable:

var dataset = [ 5, 10, 15, 20, 25 ];

Then call it after a selectAll with the element they are being added to:

d3.select("body").selectAll("p").data(dataset)

D3 can also read external files in the TSV, JSON, XML, CSV, HTML or plain old fashioned txt format.

Keep in mind you don’t have to have all the elements created on the page when you call this. You use the enter method to create a placeholder element to pass the data item to. In this example, shamelessly lifted from Scott Murray’s awesome set of D3 tutorials, the .append will create the missing elements and bind, or attach, the data elements to them. Thusly…

d3.select("body")
    .selectAll("p")
    .data(dataset)
    .enter()
    .append("p")
    .text(function(d){ return "This is data element #" + d; });

…Will get you a list of your data set. Here, the .text operator allows you  can take a function as an argument. In this case “d” is the data element that is being called that each iteration, it could be called anything. This function…

(function(d){ return "This is data element #" + d; });

… takes the input value handed to it, here defined as “d” and returns a string “This is data element #25” for the fourth data element, and so on…

The .text operators are one of a number of D3 methods that can take functions as argument. This is where D3.js starts to get really powerful in the flexibility it can offer.

In addition to accepting functions as arguments, D3 also has other methods for setting HTML attributes and CSS properties on selections. In the next set tutorial, we will cast away our inappropriate use of the Div and get into some real design, using SVG. Stayed tuned.

Feature Image: A D3.js-generated Sphere Spiral from software engineer Jason Davies.


A digest of the week’s most important stories & analyses.

View / Add Comments