Graphs with Chart.js
Deep in my heart, when I need to visualize data, I generally prefer D3.js. It allows you to quickly do almost anything that you please to datasets so large that the browser will have trouble with them before the library does. However, since D3 requires a fair amount of programming to get anything done, it doesnât fit every project, and I often want something to fill that gap.
This week, for my Morning Dashboard project, I wanted a few straightforward visualizations of some dataâIâll talk about the details more in Mondayâs developer diary postâand research led me to Chart.js.
I already knew about Chart.js, actually. You might recall my using it briefly for my post on redemption arcs and how that might apply to politics and other parts of life. I didnât do much with it, though, and I havenât had much need for data visualization on the blog, so I mostly forgot about it. But as Iâve looked at more data for the Morning Dashboard, I needed something quick.
The Basics
After including the Chart.js library on the page, we create a chart by providing a canvas
element, wrapped in a div
element. In my case, I set an explicit height on the wrapper, to ensure that the code doesnât crush down the chartâor expand it up, in some casesâto where I have trouble reading it.
<div style="height: 300px">
<canvas id="amounts">
</canvas>
</div>
Then, on loading the page, create a Chart
object on the canvas.
const ctx = document.getElementById('amounts');
new Chart(ctx, {
type: "bar",
data: {
datasets: [
{
data: [
30, 12, 4, 94, 0, 38, 65, 87,
53, 46, 58, 416, 1097, 1312, 504, 237,
713, 944, 1120, 668, 246, 107, 5, 3
],
label: "Hourly Value"
},
{
data: [
53, 46, 58, 416, 1097, 1312, 504, 237,
713, 944, 1120, 668, 246, 107, 5, 3,
30, 12, 4, 94, 0, 38, 65, 87
],
label: "Other Hourly Value"
}
],
labels: [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
]
},
options: {
maintainAspectRatio: false,
title: {
display: false
}
}
});
That produces something that looks like this chart.
Nothing too difficult, there, as long as you get things right. The chart configuration requires a typeâbar, line, etc.âone or more datasets, a shared set of labels per value in the datasets, and some options. In my case, the options allow me to set the height of the chart manually and not display a specific title to the result.
And I should mention that you get fairly nice results for this minimal effort. You get a friendly tool-tip when your mouse pointer runs over any of the bars, indicating the label (the horizontal axis), the name of the data, and the value. You can also interact with the legend, in that you can click on the different data sets to turn them on and off.
Enhancements
You can add a lot, past this point. Besides plenty of configuration options to change how the charts animate, handle layout, and so forth, each data set can take individual colors and weights.
For example, since I use this partly to visualize my television streaming, it makes sense for the chart to associate a given service with a dominantâor at least uniqueâcolor in its branding. And while bold blue and red have some overloaded meanings, enough services do have a distinct enough brand color that I can get away with this.
Pitfalls
It didnât go perfectly, though. I spent a lot of time debugging what amounted to âit stubbornly wonât work without this.â As (probably) usual, I wrote the entire post as a vector to talk about the parts that struck me as obstacles, since I actually need to remember that part, and would like to help people avoid my mishaps.
No Legend, No Chart
In particular, I had to come to terms with the necessity for every data set to have a label of its own. Since I visualize this for myself and will see the data every day, the legend mostly clutters the valuable space. This struck me as highly counter-intuitive, since I donât generally consider a legend part of its chart. However, it makes much more sense after discovering that the viewer can interact with the legend to change the view of the chart.
It made even more sense, after I discovered how to configure the legend, including turning it off, but that reminds meâŚ
Legends: Part of the Chart, or Not?
When I say âdiscovered how to configure the legend,â I mean that unpleasantly literally.
For reasons that I donât understand, the legends donât configure intuitively. Reading that I could set a legend option, I assumed that .options.legend.relevantFeature
would work. It doesnât do anything, so I made the further assumption for a while that maybe legend configuration didnât work.
It does work, but it appears thatâmuch like myselfâthe Chart.js team doesnât actually believe that Legends act as an important part of the chart. I missed a layer of indirection. In fact, to move the legend to the right-hand side of the images, I need to set .options.plugins.legend.location: right
. They consider the legend a plugin, apparentlyâŚbut you also canât display an image, unless you populate all the information that the legend wants.
I donât get it. But now I have it in a blog post, so I never have to question it again.
DIV-vy It Up
Whatever you do, try not to forget that div
elementâor presumably any element, but I didnât feel like testingâwrapping the canvas
element.
When creating this blog post, I neglected to copy the wrapper over from the source where I got it working. Instead ofâŚwell, actually, I donât know if it rendered a graph. I do know that it tried to render something infinitely tall, though, which would have made it difficult to read the graph.
Aspect Ratios
Speaking of heights, youâll apparently also generally want to set .options.maintainAspectRatio
to false. If you donât do that, the library imagines an optimal height and width for the chart and uses it, regardless of how cluttered or sparse the space becomes. By turning off the aspect ratio check, you can set the height and width of the containerâthe div
element, described aboveâto whatever shape that youâd like the graph to fill.
Error-Free, Guaranteed
Several of the other issues raise a fifth nuisance in working through this: Unless you forget a bracket or otherwise foul up the JavaScript syntax, Chart.js will never complain about what you feed it. Instead, itâll draw an empty graph, sometimes with a legend, but sometimes not, and you get to figure out what went wrong on your own time.
If I seem particularly irritated by the prior issues, I hope that this helps explain why. Rather than warning that .options.legend
doesnât exist, or that .data.datasets[0].label
wonât render, the library fails silently.
Would I Use It Again?
Complaints asideâand I find it reasonable to push four out of the five aside, since they only require me to remember something that I have now publishedâChart.js does a fairly nice job for fairly little work.
For an example of that return on effort invested, take a look at a preview for something that Iâll talk about in Mondayâs post, as well as a small peek into where I watch things.
As with the bar charts, I can toggle the lines off that donât interest me for certain questions. As a good example, since I didnât bother to hide the service names (or did IâŚ?), I could turn off the majority of services that donât cost any money, so that I can see whether I should find a particular service worth the subscription fee. Or I could toggle off the paid subscriptions, to see if I can or should lean more heavily on them, to replace another paid service.
In other words, yes, Iâll continue to work with Chart.js. It probably wonât replace D3, but unlike D3, you can probably see how straightforward the code would look to extract a range of cells from a CSV file and turn them into a complete graph. A clever person could probably even write a single JavaScript function that does this automatically, but Iâll leave that as a figurative exercise, since I donât need it.
Cheap Plug
By the way, if you find this post more interesting because it makes you wonder what I watch on Kanopy or the Roku Channel, the monthly Entropy Arbitrage newsletterâclick Follow, in the upper-right of that pageâhas a section marked Media, where I talk about my opinions on what Iâve finished watching, listening to, or reading, and comes out every month. If you want to know those opinions immediately, then you might want to become a member, because I send members previews of what I write for the newsletter.
Credits: The header image is adapted from Hand business woman pointing paper data analyze chart on desk at office by asawin, made available under the terms of the Creative Commons CC0 1.0 Universal Public Domain Dedication.
By commenting, you agree to follow the blog's Code of Conduct and that your comment is released under the same license as the rest of the blog.
Tags: linux programming techtips