Tribune DataViz

Matters of interest, from the data reporters and developers across Tribune Publishing

Author Archive

Responsive Charts with D3 and Backbone

with 2 comments

So, you started building charts with D3 and quickly realized there are certain behaviors you want all of your charts to have. Being the excellent developer you are, you decided to wrap the basics up in a nicely packaged, reusable bit of code that will help you build charts faster in the future.

For our team, this meant creating a simple Backbone view to encapsulate all of our charts’ must-haves. Why Backbone? Because we like it, though you can certainly accomplish all of what you’ll see here with a jQuery plugin or your own homebrew JavaScript lib.

Here’s how you can use it.

Simple bar chart

Just an example of a simple bar chart. If you’re not familiar with the code below, you’ll want to check out Michael Bostock’s Let’s make a bar chart.

Making your chart code reusable

At this point you’ve created a single, crappy little bar chart and you can hardly contain your joy. You can’t deny your desire to plaster these things all over your site. You WILL make this code reusable. Soon you will have loads of these things EVERYWHERE.

Before you do, however, consider a few things that might make your crappy little bar chart a little less crappy. You need:

  • Responsiviosity, responsiveness, responsivity — whatever you call it, it’s a way to redraw the chart when the window resizes
  • A simple way to extend and modify your chart to create different versions
  • A way to vary the appearance of the chart at certain viewport breakpoints
  • A fallback mechanism for browsers that don’t support D3

Enter ChartView.js — a simple Backbone view put together by my teammate David Eads to address these very needs.

So let’s wrap our bar chart up in a `ChartView` based view.

First thing we do is define `BarChartView` by extending `ChartView`. The only function we must override is the `draw` member function of `ChartView`. The code within our `draw` function looks a lot like the code we wrote to render our simple chart, but it takes advantage of some of the values that `ChartView` calculates and tracks for us. For example, instead of defining a `width` variable, we use `this.dimensions.width,` which is calculated based on the chart’s parent element.

var BarChartView = ChartView.extend({
  draw: function() {
    var scale = d3.scale.linear()
      .domain([0, d3.max(])
      .range([0, this.dimensions.width]);
      .attr('class', 'bar-chart')
          .style('width', function(d) { return scale(d) + 'px'; })
          .style('height', (this.dimensions.wrapperHeight / 5) + 'px')
          .html(function(d) { return '' + d + ''; });

The next thing we do is create a new instance (or two, or three, etc — remember charts EVERYWHERE) of our `BarChartView`. The minimum you need to get started is an options object with `el` and `data` or `collection` defined (note: you can only use one of `data` or `collection` with views that extend `ChartView` — not both).

var chart_one_data = [3, 8, 12, 7, 17];
var chart_two_data = [4, 10, 13, 14, 7];

var chart_one = new BarChartView({
  el: '#one',
  data: chart_one_data,
  base_height: 220

var chart_two = new BarChartView({
  el: '#two',
  data: chart_two_data,
  base_height: 220

Remember to call .render() or you won’t see your chart!

If you resize your browser, you’ll see these charts are now responsive. At viewport breakpoints of 420 and 728 pixels wide, the height of the charts’ containers will be adjusted to 0.7 and 0.9 of the base_height option we passed when creating them. These breakpoints are, of course, customizable:

var chart_one = new BarChartView({
  el: '#responsive-bar-chart',
  data: chart_data,
  base_height: 220,
  breakpoints: {
    728: 0.9,
    420: 0.7,
    380: 0.65

An advanced example

I know what you’re thinking. The examples are awesome, but yo, you don’t even use SVG for these charts. How about showing some real code?

You’re right. To show how to use this in a real life scenario, I thought I’d refactor some of the code we wrote for our Broken Bonds series. Trust me when I tell you you don’t want to see the original code. You can, however, see the refactored code by clicking here. Look at this code in action below.

Note that this chart has a few more options specified:

var obli_chart = new ObligationDebtChartView({
  el: '#ob_chart_container',
  collection: new Backbone.Collection(bonds),
  y_key: 'debt_per_capita',
  y_scale_max: '7e3',
  base_height: 425,
  breakpoints: {
    600: 0.75,
    380: 0.5

The `y_key` is used to determine which key to pluck from each item in our dataset to draw the chart’s bars. The `y_scale_max` option is used to adjust the maximum value that can be plotted on the chart — in this case, $7,000.

Also, notice we’re specifying a `collection`. When this option is present, `ChartView` will bind to the collection’s “sync” event, triggering a re-rendering of the chart any time the collection data changes. A big yay for events!

I won’t go through ObligationDebtChartView.js line-by-line. What’s important here is that the process is the same regardless of how complex your chart’s D3 render code is.

 draw: function() {
    return this;

We have our draw function, which calls a handful of other functions that do the heavy D3 lifting. Our chart is responsive — the bars squish as the viewport is constrained and axis labels change to be legible on smaller screens.

Fallback for older browsers

One last thing to cover — `ChartView` checks whether the browser supports D3 and will display a message if it does not.

In this example, I call .fallback_draw() directly to illustrate the point, but you should never have to do this. `ChartView` will replace the default .draw() method with .fallback_draw() when needed:

   // Fallback if d3 is unavailable, add some formatters otherwise.
    if (!this.d3) {
      this.draw = this.fallback_draw;

Again, you can customize the fallback behavior by overriding this method. For example, you might want to show an image of your chart:

  fallback_draw: function() {
    this.$el.append('<img src="" />');


Written by Ryan Nagle

March 7, 2014 at 4:12 pm

MTV: Models, templates and views in WordPress

with 11 comments

If you love WordPress and like what you’re reading here, you should work with us.

The past four months have been one big, nasty, non-stop, face-melting, brain-bending WordPress sprint for Ryan Mark and myself. We’ve been working on the migration and relaunch of ChicagoNow and finally kicked the site out the door on June 30. Please, have a look:

Along the way (read: from the very start), there were things about WordPress that we desperately wanted to be different.

Specifically, we hated writing WordPress themes.

As you may know, WordPress places no constraints on where you to put your business code. You can do complex stuff anywhere in your templates, right alongside presentation code. This makes templates really hard to read and prone to bugs. Maintainability of the code (and your quality of life) suffers.

Being the anti-freedom, inflexible, stubborn jerks that we are, we hijacked WP’s built-in template stuff along with all of its baked-in rewrite rules. In their place went Twig and a 30-line URL resolver. In order to make it easier for our Backbone.js app to interact with the WP database, we wrote a simple, consistent wrapper for WordPress patterned after Backbone.js’s models.

What we ended up with is a simple Django and Backbone inspired framework for building heavily-customized WordPress sites.

We call it MTV.

How it works

Here’s what a theme directory might look like using MTV:


As we would in Django’s, we define our urls in urls.php:

global $url_patterns; 

$url_patterns = array( 
    array('/^$/', 'theme_namespace\theme\views\home') 

Views are defined in views.php (duh):

namespace theme_namespace\theme\views; 
use mtv\shortcuts; 

function home() { 
    $str = "I want my MTV."; 
    $content = array('content' => $str); 

    shortcuts\display_template('index.html', $content); 

Our templates reside in the theme_name/templates/ directory:


In the theme’s functions.php, we setup our templating engine and our url resolver. We start by initializing Twig:

namespace theme_namespace; 

add_action('init', function() { 
    // Setup our template engine
    global $twig;


    // Specify the location of our templates
    $tmpl_path = WP_CONTENT_DIR.'/themes/theme_name/templates';

    // Create a new Twig loader object
    $loader =  new \Twig_Loader_Filesystem($tmpl_path);
    // Create a new Twig environment using $loader
    $twig = new \Twig_Environment($loader, array('debug' => true));

    // All of the functions we will allow in Twig
    // templates are housed in wp/functions.php
    include "wp/functions.php";

Next we replace all of WordPress’ rewrite rules with one of our own:

add_action('generate_rewrite_rules', function($wp_rewrite) {
    // Setup our hijack rule
    $newrules = array();
    $newrules['(.*)'] = 'index.php?path=$matches[1]';

    $wp_rewrite->rules = $newrules;
}, 1, 1);

Add ‘path’ query var that we’ll use to route requests:

add_filter('query_vars', function($vars) {
    // Where we bless query vars so WP doesn't throw them out
    $vars[] = 'path';
    return $vars;
} );

When it’s time to redirect to a template, check for our path query var and pass its value to the url resolver:

add_action('template_redirect', function() {
    global $wp_query;

    // Check for the path query var. That means we're on!
    if ( !($wp_query->query_vars['path'] === NULL) ) {

        // Load the urls from our theme

        // Load the views from our theme

        // Run the resolver with our path

        // That's all folks

About Twig templates:

Twig is pretty F’n cool. Its template syntax looks a lot like Django or Jinja.

For our home() function from the views.php example above, the index.html template might look like:

{% extends 'base.html' %}

{% block title %}MTV Example! Weeeee!{% endblock title %}

{% block content %}

        <div id="content"> {{ content }} </div> 

{% endblock content %}

The other great thing about Twig is that none of WordPress’ functions will work in its templates.

Wait, what? That’s right. You can’t use WordPress’ functions in Twig templates out of the box.

Thankfully, the overhead of making functions accessible in Twig templates is negligible. This means, however, we were forced to consider what functions we would allow in our templates.

Here’s how to make a WordPress function accessible in Twig templates, using home_url() as an example:

    global $twig;

    $twig->addFunction('home_url', new \Twig_Function_Function('home_url'));

Over time, we compiled a list of all the WordPress functions we needed to get things done. Nothing more, nothing less. And any time we try using a function that is template-prohibited, a fatal PHP error serves as a reminder of what we wanted to accomplish with MTV.

About our models

Here’s an example view using our PostCollection model to get recent posts:

    function home() {

        $args = array('post_type' => 'post',
                      'posts_per_page' => 10,
                      'order' => 'DESC');
        $posts =  PostCollection::filter($args);

        $content = array('posts' => $posts);

        shortcuts\display_template('index.html', $content);

The corresponding index.html template looks like:

    {% extends 'base.html' %}

    {% block title %}An example index template{% endblock title %}

    {% block content %}
        <div id="content">
            {% for post in posts.models %}
                    <h2>{{ post.attributes.post_title|raw }}</h2>

                    <div id="post-content">
                         {{ post.attributes.post_content|raw }}
            {% endfor %}
    {% endblock content %}

When we call:

shortcuts\display_template('index.html', $content);

Our PostCollection is passed to our template and we use a Twig for loop to display the title and content of each Post model in the collection.

Note that the $args array passed to PostCollection::filter() is exactly the same array you would pass to WP_Query() to get recent posts. The difference is that returned objects are more robust (for example, all post meta/custom fields are included as attributes of returned posts) and have methods consistent with those found in Backbone.js models (for example, get(), fetch(), set(), save()).

Also note that these Backbone-like methods do exactly what you think they do. You don’t have to remember the 101 different WordPress functions related to post objects and the variety of possible return values.

Fetch a post, get an attribute, set an attribute, save the post. Done.

One more thing to note: these models include two special methods for shuttling data back and forth between the client and server: $model->to_json() — used to send jsonified model data out to the frontend, and $model->from_json() — used to create a new model from json data sent to the backend.

I’ll leave the details of how we used these models with Backbone for a later post, but hopefully that gives an idea of how awesome these things are.

While we don’t have code ready to be open sourced just yet, we plan on doing so in the future, so stay tuned. In the meantime, we’d love to hear questions and feedback.

Written by Ryan Nagle

August 5, 2011 at 8:42 am

Posted in Uncategorized