Aug 15, 2013

Visualizing Data with D3 Part 1 — The Basics

D3 is a powerful Javascript library for manipulating the DOM based on datasets. This is the first in a series of posts in which I will cover basic techniques for building charts and graphs using D3. 

In this post, I will go through the very basics of using D3 to bind data elements to your DOM. In subsequent posts, I will expand on this building increasingly complicated and interesting charts and graphs. I will take a look at a simple one page example, located here on GitHub.

The only HTML that we start with is this (other than styles). All that we are doing here is setting up link to call “addDiv()”. Our goal here is to have D3 create a div when we click this link.

<div><a onclick="addDiv()">Add One</div>

There are four sections to the JavaScript, outlined here.

var data = [1, 2, 3, 4, 5];
drawDivs = function() {...}
addDiv = function() {...}
drawDivs();

The first line defines our initial dataset, assigning it to a global variable. Remember, in D3, everything is based off datasets. This dataset will define our initial set of divs. The next section is the drawDivs() function. This method will actually update the divs on the page. The next section is the addDiv() function. This is what will get called in our anchor tag already defined in our DOM. Finally, after everything is defined, we make a call to drawDivs(). This calls drawDivs() for the first time, initializing our page. This is where the magic happens, so let’s start there.

The first line creates our selection and binds our data to our selection.

var div = d3.select("body").selectAll(".content").data(data);

What’s going on here? We are creating a singular variable named “div” that contains the set of all divs with a class of “content”. D3 has a select() method (get one) and a selectAll() method (get all). Each of these takes a normal W3C selector. First, we select the body. This gives us one object representing the body. On that object, we call selectAll() and pass in a selector for the class “content”. This returns an object representing everything on the page that matches the selector. The first time we call this method, this collection will be empty (remember, the only thing on the DOM to start is the “Add New” anchor). Next we call the data method on this object, binding our data set to it.

At this point, it is important to take note that D3 relies heavily on method chaining. A method call on a D3 object will often return that object, allowing for chaining and more concise code.

Now what? The variable “div” contains this collection with our data bound to it. This object contains three joins, the enter, update, and exit join. In the enter join, we can manipulate new data bindings. In the update join, we manipulate all data bindings. In the exit join we manipulate all bindings that no longer exist. The first time through, everything is in then enter join, as nothing matching the selector yet exists in the DOM. So let’s deal with the enter join. To do so, we call the enter() method.

div.enter().append("div").attr("class", "content");

Here we are telling the D3 div variable that for each item in the enter join, we want to append a div, and each of those should have a class attribute with a value of content. Remember that our initial selection was selected off of the body tag. Also remember that the first time through this method, we have five elements in the enter join, as defined by the global data array. Thus, the end result of this will be to append at the end of the body element this line five times:

<div class="content"></div>

In this next section, we chain two calls onto the update join. Remember that this join contains every data element in the D3 object, div.

div.style("width", function(d){ 
    return "" + (10 * d) + "px";
})
.on("click", function(d) {
    for(var i=0;i<data.length;i++){
        if(data[i]==d){data.splice(i,1);break;}
    }
    drawDivs();
});

It looks like there’s a lot going on here, so let’s break it down. The first call is a call to the style() method. Like it’s name implies, this will modify the style attribute of each element in the join. We want to adjust the width. We could make a call like this:

div.style("width", "100px");

This would be rather boring though. Instead, we want the width of each div to be a function of the datum bound to it. D3 allows us to pass in a function to handle this. Our function takes one parameter d, which is the datum bound to that element. Note: this function can also take a second parameter, which is the index into the data array that this datum exists at. Here we simply return a pixel width that is a multiple of the datum.

Next, we call the on() method. The on() method binds to event listeners. In this case, it will add an onClick to each div. What are we doing here? We want to remove a div by clicking on it. We bind a function to the click event that splices the data from the data array and re-calls drawDivs() with the new dataset. (Observant readers may note that it would have been more efficient to take advantage of the optional index parameter. We will use that parameter more in subsequent posts).

Wait… If we can remove elements, what happens to them? You may recall that elements that are no longer bound to our dataset get put in the exit join. We deal with that in the next line:

div.exit().remove();

Here we call exit() to get access to each method in the exit join, and call remove on each of them. This method removes them from the DOM.

That is our entire drawDivs() method. At this point, the addDiv() method is probably obvious to you:

addDiv = function() {
    data[data.length] = data[data.length - 1] + 1;
    drawDivs();
}

This method simply adds a new data element to our array. For fun, it is one larger than the last entry in the array. Then it calls drawDivs() to let D3 manipulate the DOM for us.

That is our simple example of manipulating the DOM using D3. Please grab the HTML file from GitHub and load it up in your browser and experiment with it. Over the course of the next few posts, I will walk through increasingly complicated techniques for displaying charts and graphs using D3 with the HTML5 SVG tags.

About the Author

Object Partners profile.
Leave a Reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]