Render D3.js-driven SVG server-side!

Recently I’ve been working on a congressional tweet aggregator to get a handle on what our legislators are saying. To make that easier to see, I figured I’d start adding some charts and lists and other snazzy dataviz gizmos that are so hot these days.

I like D3.js as a graphing library. It makes clean, interactive, data-driven charts a snap to make in just a few lines of Javascript. Then it does its magic to render the data in crisp SVG, which I am quite fond of. On my site, I wanted to turn the crank on the back-end for charts that don’t update all that frequently, inject them into my templates, and spare the viewers of my site the heavy-lifting required for multiple charts—not to mention my poor server that has to execute several complicated queries to do the appropriate counting to generate the data to back the charts.

After a little poking around on the internet, I stumbled on to PhantomJS, which bills itself as a full-stack headless WebKit. Perfect. It can ping my website periodically, load the chart pages, and extract the SVG, I thought.

Not so fast. The Phantom is excellent at reading SVG, and it’s even good at rendering it to PDF or PNG. But that’s not what I wanted! I just wanted it to spit out the SVG for me after D3 was finished making it, untouched. And since SVG elements don’t have an innerHTML property, I needed to think harder to find a solution; i.e., ask Google. But Google didn’t seem to know, either. So I wrote a tiny script to extract page elements by ID. Maybe one of you will find it useful, too.

var system = require('system');

if (system.args.length != 3) {
    console.log("Usage: extract.js  ");
    phantom.exit(1);
}

var address = system.args[1];
var elementID = system.args[2];
var page = require('webpage').create();

function serialize(elementID) {
    var serializer = new XMLSerializer();
    var element = document.getElementById(elementID);
    return serializer.serializeToString(element);
}

function extract(elementID) {
  return function(status) {
    if (status != 'success') {
      console.log("Failed to open the page.");
    } else {
      var output = page.evaluate(serialize, elementID);
      console.log(output);
    }
  phantom.exit();
  };
}

page.open(address, extract(elementID));

5 thoughts on “Render D3.js-driven SVG server-side!

    • The content of extract.js is what I posted above. Just copy and paste it into a new plain-text file and save it. In order to run it, you’ll need to download PhantomJS.

      Now to extract a particular HTML element from a webpage, run the script with the URL and element’s ID as arguments. (This script only works for elements that can be referenced by ID.) For example, to fetch the graph on my Congress Birdie website of bills introduced in 2012—it’s an SVG element called timeline—you’d type:

      phantomjs extract.js http://congress.joshreyes.com timeline

      I hope that helps.

  1. Pingback: Kino jest

Comments are closed.