The past couple of weeks have been a blur of JavaScript and data. I’ve been buried in USDA reports researching two various possibilities for my summer project — something I’d better hop on as the summer is rapidly disappearing!
For the past couple of weeks, I’ve been working off and on on this infographic about the amount of debt that foreign countries hold. Credit for the analysis goes to coworker Todd Lindeman, who created the graphic for print and then gave it to me to make interactive.
My supervisor showed me a graphic that the department had done in Flash a couple of years prior with a similar chart. My instructions: Do this, but in Javascript.
There were two huge hurdles in this project. One was finding a jQuery graphing library that supported area chart hovers. The other was IE7 support.
I settled on jqPlot for the graphs. It was the only one I found that seemed to natively support area graph hovers, something I figured would save me a lot of time.
What I wasn’t figuring on was jqPlot’s horrible, horrible documentation. It’s basically an API reference, not real user documentation. (I eventually found the jqPlot user group, the most helpful post of which was this thread on deep customization.)
Breakdown of frustrations with the project (many of which, no doubt, are due to my inexperience):
Tooltip support
In Flot, I could have done something like:
function showTooltip(x, y, contents) {
$(‘
‘ + contents + ‘
‘).appendTo(“body”).fadeIn(200);
}
…and it would have just worked with whatever info was supplied in contents() — or, you know, whatever custom function you wanted to write and display. I wanted to display the name of the country whose graph the mouse was over. I had this in a variable that incremented or decremented depending on the data series.
In jqPlot, not only do you have to include a “plugin” to enable the tooltip, but the part of the code that renders the contents of the tooltip looks something like this:
…
var elem = document.createElement(‘div’);
c._tooltipElem = $(elem);
elem = null;
c._tooltipElem.addClass(‘jqplot-cursor-tooltip’);
c._tooltipElem.css({position:’absolute’, display:’none’});
…
if (c.showTooltipUnitPosition){
if (c.tooltipAxisGroups.length === 0) {
var series = this.series;
var s;
var temp = [];
for (var i=0; i
s = series[i];
var ax = s.xaxis+','+s.yaxis;
if ($.inArray(ax, temp) == -1) {
temp.push(ax);
}
}
for (var i=0; i
c.tooltipAxisGroups.push(temp[i].split(','));
}
}
}
...
Er, what?
So, yeah, I ended up stealing some super-simple tooltip code and just using this when a data series was highlighted:
function (ev, seriesIndex, pointIndex, data) {
var countryName, debtHeld;
if (seriesIndex == 6) {
…
countryName = “China”;
debtHeld = china.slice(-1);
$(‘#tooltip’).show();
$(‘#tooltip’).html(‘
‘ + countryName + ‘ holds $’ + debtHeld + ‘ billion in U.S. debt.
(click for detail view)
‘);
}
…
Custom axis labels
The way jqPlot wants data provided is in pairs: [[0, 50], [2.5,47]]…
That wasn’t how my data was formatted, and it didn’t readily occur to me that I could have probably wrangled it in Excel (oops). So instead of being able to tell it, “use this set of data to plot the x axis, but only show 10 tick marks,” it wanted to show all 126 tick marks. On a 600px wide graph. Yeeaaaah, not so much. I wrote a for loop to only show every 12th one (I only wanted to show January of each year), but then realized that the chart started in March 2000 and ended in April 2011. To save myself time, I just overwrote the CSS and plopped an image in for the axis ticks and text for the labels. Cheap hack, but it worked.
For the y axis, I couldn’t figure out how to tell it “round my numbers for the labels only.” I figured I could probably have just stuck a Math.round() on something, but that would have been too easy… I also wanted to convert the numbers, which were in billions, to trillions (because saying Asia holds 4000-some-odd billions is just weird).
Same fix as the x axis. Boo. There is absolutely no reason I shouldn’t have been able to achieve that with JS.
IE7
Okay, when is Internet Explorer NOT a frustration? In this case, though, part of it was a case of what I should have done versus what I did.
How I wrote my code:
I replotted the data every time someone clicked a continent or country, using the same div as the canvas each time. While a fast computer running Chrome shouldn’t even blink, IE7 slogged along, chewing on the data.
How I should have written my code:
On page load, process all the data and draw each chart in its own div. Hide the divs, and just show them with jQuery on click. Slower on the load time, but faster when you’re clicking around.
The other part? IE7 hates jQuery’s .append() call. .appendTo() supposedly works, but I couldn’t get it to (perhaps because I was trying to append to a hidden div). I ended up creating a different set of headers for each continent, and just using .show()/.hide() to pull them up. Ugly, messy solution.
Animation
The Flash graphic my boss originally showed me allowed you to select a large portion of the graph, then animated it in a sort of melting, expanding fashion, and showed you the detail.
One of the issues I have with many interactives is extraneous animation. In this case, the animation wasn’t extraneous. Its purpose was to draw your attention to the fact that you were actually zooming in, something that isn’t immediately apparent when you’re just rerendering on a graph *and* changing the y axis.
I’m sure there would be a way to animate a series of data points (slide them all to baseline 0 on click, for instance), but I made that my last priority and ended up just using .fade() to change between them. (That in itself was a pain, as the div would fade out, but show a flash of the new chart before it finished. I fixed that by appending the function that draws the chart as a callback to the fade.) But really, now all I want to do is figure out how that animation would work…
Running out of time
I wish I’d had time to pitch one additional feature. I would have liked to have added a small map of the corner of the world currently being examined. Americans are stereotypically ignorant about the location of other countries (while trying to determine the plurality of “The Philippines,” for instance, I got an autocomplete for “is the philippines…” of “…in Asia”). I had the white space for it. All I needed was to create a CSS sprite that would have highlighted various countries as their corresponding debt was moused over.
Ah well, there’s always next time!