Hard at work in the D3 mines

Why is D3 So Hard To Learn From Bl.ocks?

Tantalizingly complete, the current state of blocks leads to confusion and poor coding technique.

Nightingale
Published in
6 min readSep 11, 2019

--

Comments Save Brains

As an early career practitioner, I’m drawn to D3 by the compelling graphics I’ve seen users produce with it, as well as its interactivity. As a former mechanical engineer, my nature is to want to play with and manipulate things. D3 is often touted as the only way to make the most powerful data visualization products. But one of the main ways of learning D3 is “by example” (see Mike Bostock’s 2013 Eyeo talk “For Example”) and those examples are typically hosted as gists on GitHub and then deployed using Mike’s own code called bl.ocks.org (typically referred to as “blocks”) or via similar methods like blockbuilder. Bostock’s new endeavor, Observable, is populated with many new examples that also follow the same approach as these original blocks.

I want to start off by thanking him for creating D3. It’s an incredible tool and I very much look forward to feeling competent in my use of it.

Unfortunately, these blocks he created show major issues that can subvert a learner’s ability to understand code. Or maybe they started that way? I’m new enough to D3 to not know the origin story of these things. Did they start as innocuous ramblings from Bostock as he played with the magical toy he had just invented? Did he intend for them to become the go-to tool for learning how to use said magical toy? Did he ever imagine how many hours would be spent by future disciples helplessly trying to decode not only his wholly un-commented work but the un-commented, and frequently wrong, work of others?

I like to think that it was the innocuous ramblings option. That way I can excuse Bostock and those that came after him from culpability in my personal frustration as I learn his tool.

Here’s an example of a fun viz that would be so much more useful to us newbies if it had some comments.

https://bl.ocks.org/mbostock/3231298
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
height = 500;

var nodes = d3.range(200).map(function() { return {radius: Math.random() * 12 + 4}; }),
root = nodes[0],
color = d3.scale.category10();

root.radius = 0;
root.fixed = true;

var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) { return i ? 0 : -2000; })
.nodes(nodes)
.size([width, height]);

force.start();

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);

svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", function(d, i) { return color(i % 3); });

force.on("tick", function(e) {
var q = d3.geom.quadtree(nodes),
i = 0,
n = nodes.length;

while (++i < n) q.visit(collide(nodes[i]));

svg.selectAll("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});

svg.on("mousemove", function() {
var p1 = d3.mouse(this);
root.px = p1[0];
root.py = p1[1];
force.resume();
});

function collide(node) {
var r = node.radius + 16,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + quad.point.radius;
if (l < r) {
l = (l - r) / l * .5;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}

</script>

In the example above, wouldn’t it be great to have a brief explanation of what the map method is doing? Or what do .gravity and .charge do? These, of course, are things that can be looked up in the D3 API but a brief explanation specific to this chart would be much more efficient.

I’ve been learning D3 on and off now for about a year and a half. I’ve gone through some tutorials but I find that they only scratch the surface and are often fragmented — learning the code for building a scale doesn’t mean anything to me without the accompanying chart. Also, I’m impatient. Once I built my first simple bar chart I wanted to do all of it. And all of it was sitting right there in the block library! Beautiful, interactive charts that just looked so cool. And all the code was sitting there for me to use!

So, my process has generally been the following:

  1. Decide what kind of chart I want to make
  2. Search for a block demonstrating that chart
  3. Attempt to modify the block to ingest my own data and fit my own needs.

This is where things go wrong. Without comments and explanations of what is happening along the way, it’s very hard for someone starting out to figure out what’s going on. Where in this code is the data ingested? How is it manipulated? Why is it manipulated? And why isn’t it working with my data?

And then there’s the problem of blocks that are written with bad code, or don’t include the necessary data files or libraries. I run a dataviz meetup near me and part of what we’ve been doing each month is to dig through a block to understand what’s going on. We’ve come across:

  • Redundant code. This is where the writer wasn’t sure which was the best method to accomplish their task so they just used both. How is a newbie supposed to recognize this?
  • Blocks that don’t include all the necessary data files. The only way to recreate a block is to have the data it worked with. Without that it’s useless.
  • Blocks that are disorganized. This is a stylistic issue and one I could live with but it’s worth mentioning.

Blocks today seem to be simply a showpiece. A way to get someone’s work up in front of other people. They’re not written or intended to be helpful or useful in the pursuit of learning D3. I should also say, aside from HTML and CSS, this is the first coding language I’ve learned so I don’t know if my experience with D3 is unique to this language or just the way it’s done in learning to code.

What can we do to fix this?

In writing this article I’ve stumbled upon some maybe useful articles and tutorials that I wish I had read through as I was starting out (but I’m impatient). The D3 Tutorials Github Repo appears to have some more comprehensive information.

I, myself, have been working to comment any block I dig through and fix bad code. I hope others will join me. I’ve only gotten a few done but every little bit helps.

  • Comment your blocks. Write nice long intros that explain what the block does and any challenges you had. Especially comment on any data binding peculiarities.
  • Include any data files that are needed for the block. That should go without saying.
  • Include links to any additional libraries you used.
  • Make note, ideally in the title but at least in the intro, what version you used to create the block. Versions matter a lot.
  • Write an awesomely detailed account of how you created your thing like this guy.

Overall, try to remember what it was like when you were learning this tool and think back to what might have helped you as you sat mystified at your desk. When I first started learning this tool I thought it was so cool that there were all these great examples for me to work off of. A year and a half into it, now, the tool is finally starting to gain some transparency. I wonder how much faster that could have happened if those examples had been easier to understand.

--

--