At work I was tasked with creating an internal tool to track and display vital company stats. I knew that meant I would likely be displaying some data in charts and graphs. Thus began my search for an easily customizable data visualization library.

I perused the roundup of libraries that Google and Stack Overflow offered: Highcharts.js, Flot, gRaphael, jsCharts, and finally d3.js (I hadn’t seen Chart.js at the time, but it looks pretty nice). I won’t go into comparisons on the various libraries, but I will tell you why I chose d3.js:

  • easy to use (after the beginner’s hump)
  • uses svg: scalable & widely supported
  • completely customizable
  • flexible & extendable
  • extremely powerful
  • lots of examples
  • even more examples

At the top of this article I’ve inserted a simple bar chart as an example. You can see some screenshots of actual real-time data visualizations I created for our company monitor. Let’s take a look at how D3 makes these animating bar charts:

First, let’s drop in a few simple styles:

svg {
  margin: 2em 0;
  font: 10px sans-serif;
}

.foreground {
  fill: #2D6A99;
}

.background {
  fill: #eee;
}

And of course we need to include the d3.js library:

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

Now, let’s get some basic moving parts. We need to setup what a bar is and establish initial variables:

var n = 10, // how many bars?
    random = function() { return Math.floor(Math.random() * 100); }, // randomize some numbers for data
    data = d3.range(n).map(random); // an array of randomized datapoints

var barChart = {
  init: function() {
    this.height = 100;
    this.width = 220;
    this.padding = 12;
    this.el = "article header"; // where we'll put our svg 

    // calculate the bar width from the total chart width
    barWidth = Math.floor((this.width - (this.padding * (data.length - 1))) / data.length);
    barHeight = this.height - 20;


    this.svg = d3.select(this.el).insert('svg', ':first-child')
      .attr('width', this.width)
      .attr("height", this.height);

  }
}

Now, we have to actually render it:

var barChart = {

  init: function() { /***/ },

  // draw our svg container
  draw: function() {
    this.meters = this.svg
      .append("g")
        .attr("class", "meter")
        .selectAll("rect")
          .data(data)
          .enter()
          .append('g')
            .attr("class", "bar");

    this.drawBar().attr("class", "background").attr("y", 0).attr("height", barHeight);
    this.drawBar().attr("class", "foreground").attr("y", barHeight).attr("height", 0);
  },

  // this actually draws a bar
  drawBar: function () {
    var self = this;

    return this.meters.append("rect")
      .attr("x", function (d, i) {
        return i * (barWidth + self.padding);
      })
      .attr("width", barWidth);
  }
}

// initialize the bar chart!
barChart.init();

Ok. So this is what we’ve got so far:

Check out this Pen!

Alright, now let’s animate this shiz.

var barChart = {

  init:     function() { /***/ },
  draw:     function() { /***/ },
  drawBar:  function() { /***/ },

  update: function () {
      var self = this;
      // select the foreground bars and call the animate method on them
      d3.selectAll("rect.foreground").each(self.animate);
  },

  animate: function (d, i) {
    var total = data[i];
    var bar = d3.select(this);
    if (barHeight - total != bar.attr("y")) {
      bar.transition().duration(1500).attr("height", total).attr("y", barHeight - total);
    }
  }
}

// In this example, every couple seconds update the bar chart with new numbers!
setInterval(function() {
  data = d3.range(n).map(random); // get new random numbers
  barChart.update();
}, 2000);
The final result

Check out the code in action on CodePen