Hand roll charts with D3 like you actually know what you’re doing

Charts! My least favorite subject besides Social Studies. But you just won’t get very far in this industry before someone wants you to make a chart. I don’t know what it is with people and charts, but apparently we can’t have a civilization without a bar chart showing Maggie’s sales for last month so by ALL MEANS — let’s make a chart.

Yes, I know this is not how you would display this data. I’m trying to make a point here.

To prepare you for that impending “OMG I’m going to have to make a chart” existential crisis that, much like death, we like to pretend is never going to happen, I’m going to show you how to hand-roll your own scatter plot graph with D3.js. This article is heavy on the code side and your first glance at the finished code is going to trigger your “fight or flight” response. But if you can get through this article, I think you will be surprised at how well you understand D3 and how confident you are that you can go make some other chart that you would rather not make.

Before we do that, though, it’s important to talk about WHY you would ever want to roll your own chart.

Building vs. Buying

When you do have to chart, you will likely reach for something that comes “out of the box.” You would never ever hand-roll a chart. The same way you would never sit around and smash your thumb with a hammer; it’s rather painful and there are more productive ways to use your hammer. Charts are rather complex user interface items. It’s not like you’re center-aligning some text in a div here. Libraries like Chart.js or Kendo UI have pre-made charts that you can just point at your data. Developers have spent thousands of hours perfecting these charts You would never ever build one of these yourself.

Or would you?

Charting libraries are fantastic, but they do impose a certain amount of restrictions on you…and sometimes they actually make it harder to do even the simple things. As Peter Parker’s grandfather said before he over-acted his dying scene in Spiderman, “With great charting libraries, comes great trade-off in flexibility.”

Toby never should have been Spiderman. FITE ME.

This is exactly the scenario I found myself in when my colleague, Jasmine Greenaway, and I decided that we could use charts to figure out who @horse_js is. In case you aren’t already a big @horse_js fan, it’s a Twitter parody account that quotes people out of context. It’s extremely awesome.

We pulled every tweet from @horse_js for the past two years. We stuck that in a Cosmos DB database and then created an Azure Function endpoint to expose the data.

And then, with a sinking feeling in our stomachs, we realized that we needed a chart. We wanted to be able to see what the data looked like as it occurred over time. We thought being able to see the data visually in a Time Series Analysis might help us identify some pattern or gain some insight about the twitter account. And indeed, it did.

We charted every tweet that @horse_js has posted in the last two years. When we look at that data on a scatter plot, it looks like this:

See the Pen wYxYNd by Burke Holland (@burkeholland) on CodePen.

Coincidentally, this is the thing we are going to build in this article.

Each tweet is displayed with the date on the x-axis, and the time of day on the y. I thought this would be easy to do with a charting library, but all the ones I tried weren’t really equipped to handle the scenario of a date across the x and a time on the y. I also couldn’t find any examples of people doing it online. Am I breaking new ground here? Am I a data visualization pioneer?

Probably. Definitely.

So, let’s take a look at how we can build this breathtaking scatter plot using D3.

Getting started with D3

Here’s the thing about D3: it looks pretty awful. I just want to get that out there so we can stop pretending like D3 code is fun to look at. It’s not. There’s no shame in saying that. Now that we’ve invited that elephant in the room to the tea party, allow me to insinuate that even though D3 code looks pretty bad, it’s actually not. There’s just a lot of it.

To get started, we need D3. I am using the CDN include for D3 5 for these examples. I’m also using Moment to work with the dates, which we’ll get to later.

https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js

D3 works with SVG. That’s what it does. It basically marries SVG with data and provides some handy pre-built mechanisms for visualization it — things such as axis. Or Axees? Axises? Whatever the plural of “axis” is. But for now, just know that it’s like jQuery for SVG.

So, the first thing we need is an SVG element to work with.

<svg id="chart"></svg>

OK. Now we’re ready to start D3’ing our way to data visualization infamy. The first thing we’re going to do is make our scatter plot a class. We want to make this thing as generic as possible so that we can re-use it with other sets of data. We’ll start with a constructor that takes two parameters. The first will be the class or id of the element we are about to work with (in our case that’s, #chart) and the second is an object that will allow us to pass in any parameters that might vary from chart-to-chart (e.g. data, width, etc.).

class ScatterPlot { constructor(el, options) { }
}

The chart code itself will go in a render function, which will also require the data set we’re working with to be passed.

class ScatterPlot { constructor(el, options) { this.render(options.data); } render(data) { }
}

The first thing we’ll do in our render method is set some size values and margins for our chart.

class ScatterPlot { constructor(el, options) { this.data = options.data || []; this.width = options.width || 500; this.height = options.height || 400; this.render(); } render() { let margin = { top: 20, right: 20, bottom: 50, left: 60 }; let height = this.height || 400; let width = (this.height || 400) - margin.top - margin.bottom; let data = this.data; }
}

I mentioned that D3 is like jQuery for SVG, and I think that analogy sticks. So you can see what I mean, let’s make a simple SVG drawing with D3.

For starters, you need to select the DOM element that SVG is going to work with. Once you do that, you can start appending things and setting their attributes. D3, just like jQuery, is built on the concept of chaining, so each function that you call returns an instance of the element on which you called it. In this manner, you can keep on adding elements and attributes until the cows come home.

For instance, let’s say we wanted to draw a square. With D3, we can draw a rectangle (in SVG that’s a rect), adding the necessary attributes along the way.

See the Pen zmdpJZ by Burke Holland (@burkeholland) on CodePen.

NOW. At this point you will say, “But I don’t know SVG.” Well, I don’t either. But I do know how to Google and there is no shortage of articles on how to do pretty much anything in SVG.

So, how do we get from a rectangle to a chart? This is where D3 becomes way more than just “jQuery for drawing.”

​​First, let’s create a chart. We start with an empty SVG element in our markup. We use D3 to select that empty svg element (called #chart​) and define its width and height as well as margins.

// create the chart
this.chart = d3.select(this.el) .attr('width', width + margin.right + margin.left) .attr('height', height + margin.top + margin.bottom);

And here’s what it looks like:

See the Pen EdpOqy by Burke Holland (@burkeholland) on CodePen.

AMAZING! Nothing there. If you open the dev tools, you’ll see that there is something there. It’s just an empty something. Kind of like my soul.

That’s your chart! Let’s go about putting some data in it. For that, we are going to need to define our x and y-axis.

That’s pretty easy in D3. You call the axisBottom method. Here, I am also formatting the tick marks with the right date format to display.

let xAxis = d3.axisBottom(x).tickFormat(d3.timeFormat('%b-%y'));

I am also passing an “x” parameter to the axisBottom method. What is that? That is called a scale.

D3 scales

D3 has something called scales. Scales are just a way of telling D3 where to put your data and D3 has a lot of different types of scales. The most common kind would be linear — like a scale of data from 1 to 10. It also contains a scale just for time series data — which is what we need for this chart. We can use the scaleTime method to define a “scale” for our x-axis.

// define the x-axis
let minDateValue = d3.min(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY'));
}); let maxDateValue = d3.max(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY'));
}); let x = d3.scaleTime() .domain([minDateValue, maxDateValue]) .range([0, width]); let xAxis = d3.axisBottom(x).tickFormat(d3.timeFormat('%b-%y'));

D3 scales use some terminology that is slightly intimidating. There are two main concepts to understand here: domains and ranges.

  • Domain: The range of possible values in your data set. In my case, I’m getting the minimum date from the array, and the maximum date from the array. Every other value in the data set falls between these two endpoints — so those “endpoints” define my domain.
  • Range: The range over which to display your data set. In other words, how spread out do you want your data to be? In our case, we want it constrained to the width of the chart, so we just pass width as the second parameter. If we passed a value like, say, 10000, our data out over 10,000 pixels wide. If we passed no value at all, it would draw all of the data on top of itself all on the left-hand side of the chart… like the following image.

The y-axis is built in the same way. Only, for it, we are going to be formatting our data for time, not date.

// define y axis
let minTimeValue = new Date().setHours(0, 0, 0, 0);
let maxTimeValue = new Date().setHours(23, 59, 59, 999); let y = d3.scaleTime() .domain([minTimeValue, maxTimeValue]) .nice(d3.timeDay) .range([height, 0]); let yAxis = d3.axisLeft(y).ticks(24).tickFormat(d3.timeFormat('%H:%M'));

The extra nice method call on the y scale tells the y-axis to format this time scale nicely. If we don’t include that, it won’t have a label for the top-most tick on the left-hand side because it only goes to 11:59:59 PM, rather than all the way to midnight. It’s a quirk, but we’re not making crap here. We need labels on all our ticks.

Now we’re ready to draw our axis to the chart. Remember that our chart has some margins on it. In order to properly position the items inside of our chart, we are going to create a grouping (g) element and set its width and height. Then, we can draw all of our elements in that container.

let main = this.chart.append('g') .attr('transform', `translate(${margin.left}, ${margin.top})`) .attr('width', width) .attr('height', height) .attr('class', 'main');

We’re drawing our container, accounting for margin and setting its width and height. Yes. I know. It’s tedious. But such is the state of laying things out in a browser. When was the last time you tried to horizontally and vertically center content in a div? Yeah, not so awesome prior to Flexbox and CSS Grid.

Now, we can draw our x-axis:

main.chart.append('g') .attr('transform', `translate(0, ${height})`) .attr('class', 'main axis date') .call(xAxis);

We make a container element, and then “call” the xAxis that we defined earlier. D3 draws things starting at the top-left, so we use the transform attribute to offset the x-axis from the top so it appears at the bottom. If we didn’t do that, our chart would look like this…

By specifying the transform, we push it to the bottom. Now for the y-axis:

main.append('g') .attr('class', 'main axis date') .call(yAxis);

Let’s look at all the code we have so far, and then we’ll see what this outputs to the screen.

class ScatterPlot { constructor(el, options) { this.el = el; if (options) { this.data = options.data || []; this.tooltip = options.tooltip; this.pointClass = options.pointClass || ''; this.data = options.data || []; this.width = options.width || 500; this.height = options.height || 400; this.render(); } } render() { let margin = { top: 20, right: 15, bottom: 60, left: 60 }; let height = this.height || 400; let width = (this.width || 500) - margin.right - margin.left; let data = this.data; // create the chart let chart = d3.select(this.el) .attr('width', width + margin.right + margin.left) .attr('height', height + margin.top + margin.bottom); // define the x-axis let minDateValue = d3.min(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY')); }); let maxDateValue = d3.max(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY')); }); let x = d3.scaleTime() .domain([minDateValue, maxDateValue]) .range([0, width]); let xAxis = d3.axisBottom(x).tickFormat(d3.timeFormat('%b-%y')); // define y axis let minTimeValue = new Date().setHours(0, 0, 0, 0); let maxTimeValue = new Date().setHours(23, 59, 59, 999); let y = d3.scaleTime() .domain([minTimeValue, maxTimeValue]) .nice(d3.timeDay) .range([height, 0]); let yAxis = d3.axisLeft(y).ticks(24).tickFormat(d3.timeFormat('%H:%M')); // define our content area let main = chart.append('g') .attr('transform', `translate(${margin.left}, ${margin.top})`) .attr('width', width) .attr('height', height) .attr('class', 'main'); // draw x axis main.append('g') .attr('transform', `translate(0, ${height})`) .attr('class', 'main axis date') .call(xAxis); // draw y axis main.append('g') .attr('class', 'main axis date') .call(yAxis); }
}

See the Pen oaeybM by Burke Holland (@burkeholland) on CodePen.

We’ve got a chart! Call your friends! Call your parents! IMPOSSIBLE IS NOTHING!

​​Axis labels

Now let’s add some chart labels. By now you may have figured out that when it comes to D3, you are doing pretty much everything by hand. Adding axis labels is no different. All we are going to do is add an svg text​ element, set it’s value and position it. That’s all.
​​
​​For the x​-axis, we can add the text label and position it using translate​. We set it’s x​ position to the middle (width / 2) of the chart. Then we subtract the left-hand margin to make sure we are centered under just the chart. I’m also using a CSS class for axis-label​ that has a text-anchor: middle​ to make sure our text is originating from the center of the text element.
​​

​​​​// text label for the x axis
​​chart.append("text") ​​ .attr("transform",
​​ "translate(" + ((width/2) + margin.left) + " ," + ​​ (height + margin.top + margin.bottom) + ")")
​​ .attr('class', 'axis-label')
​​ .text("Date Of Tweet");

​​
​​The y​-axis is the same concept — a text​ element that we manually position. This one is positioned with absolute x​ and y​ attributes. This is because our transform​ is used to rotate the label, so we use the x​ and y​ properties to position it.
​​
​​Remember: Once you rotate an element, x and y rotate with it. That means that when the text​ element is on its side like it is here, y​ now pushes it left and right and x​ pushes it up and down. Confused yet? It’s OK, you’re in great company.
​​

​​// text label for the y-axis
​​chart.append("text")
​​ .attr("transform", "rotate(-90)")
​​ .attr("y", 10)
​​ .attr("x",0 - ((height / 2) + (margin.top + margin.bottom))
​​ .attr('class', 'axis-label')
​​ .text("Time of Tweet - CST (-6)");

​​
​​

See the Pen oaeybM by Burke Holland (@burkeholland) on CodePen.

​​Now, like I said — it’s a LOT of code. That’s undeniable. But it’s not super complex code. It’s like LEGO: LEGO blocks are simple, but you can build pretty complex things with them. What I’m trying to say is it’s a highly sophisticated interlocking brick system.

​​Now that we have a chart, it’s time to draw our data.
​​

Drawing the data points

This is fairly straightforward. As usual, we create a grouping to put all our circles in. Then we loop over each item in our data set and draw an SVG circle. We have to set the position of each circle (cx and cy) based on the current data item’s date and time value. Lastly, we set its radius (r), which controls how big the circle is.

let circles = main.append('g'); data.forEach(item => { circles.append('svg:circle') .attr('class', this.pointClass) .attr('cx', d => { return x(new Date(item.created_at)); }) .attr('cy', d => { let today = new Date(); let time = new Date(item.created_at); return y(today.setHours(time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds())); }) .attr('r', 5);
});

When we set the cx and cy values, we use the scale (x or y) that we defined earlier. We pass that scale the date or time value of the current data item and the scale will give us back the correct position on the chart for this item.

And, my good friend, we have a real chart with some real data in it.

See the Pen VEzdrR by Burke Holland (@burkeholland) on CodePen.

Lastly, let’s add some animation to this chart. D3 has some nice easing functions that we can use here. What we do is define a transition on each one of our circles. Basically, anything that comes after the transition method gets animated. Since D3 draws everything from the top-left, we can set the x position first and then animate the y. The result is the dots look like they are falling into place. We can use D3’s nifty easeBounce easing function to make those dots bounce when they fall.

data.forEach(item => { circles.append('svg:circle') .attr('class', this.pointClass) .attr('cx', d => { return x(new Date(item.created_at)); }) .transition() .duration(Math.floor(Math.random() * (3000-2000) + 1000)) .ease(d3.easeBounce) .attr('cy', d => { let today = new Date(); let time = new Date(item.created_at); return y(today.setHours(time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds())); }) .attr('r', 5);

OK, so one more time, all together now…

class ScatterPlot { constructor(el, options) { this.el = el; this.data = options.data || []; this.width = options.width || 960; this.height = options.height || 500; this.render(); } render() { let margin = { top: 20, right: 20, bottom: 50, left: 60 }; let height = this.height - margin.bottom - margin.top; let width = this.width - margin.right - margin.left; let data = this.data; // create the chart let chart = d3.select(this.el) .attr('width', width + margin.right + margin.left) .attr('height', height + margin.top + margin.bottom); // define the x-axis let minDateValue = d3.min(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY')); }); let maxDateValue = d3.max(data, d => { return new Date(moment(d.created_at).format('MM-DD-YYYY')); }); let x = d3.scaleTime() .domain([minDateValue, maxDateValue]) .range([0, width]); let xAxis = d3.axisBottom(x).tickFormat(d3.timeFormat('%b-%y')); // define y axis let minTimeValue = new Date().setHours(0, 0, 0, 0); let maxTimeValue = new Date().setHours(23, 59, 59, 999); let y = d3.scaleTime() .domain([minTimeValue, maxTimeValue]) .nice(d3.timeDay) .range([height, 0]); let yAxis = d3.axisLeft(y).ticks(24).tickFormat(d3.timeFormat('%H:%M')); // define our content area let main = chart.append('g') .attr('transform', `translate(${margin.left}, ${margin.top})`) .attr('width', width) .attr('height', height) .attr('class', 'main'); // draw x axis main.append('g') .attr('transform', `translate(0, ${height})`) .attr('class', 'main axis date') .call(xAxis); // draw y axis main.append('g') .attr('class', 'main axis date') .call(yAxis); // text label for the y axis
​​ chart.append("text")
​​ .attr("transform", "rotate(-90)")
​​ .attr("y", 10)
​​ .attr("x",0 - ((height / 2) + margin.top + margin.bottom)
​​ .attr('class', 'axis-label')
​​ .text("Time of Tweet - CST (-6)"); ​​ ​​ // draw the data points let circles = main.append('g'); data.forEach(item => { circles.append('svg:circle') .attr('class', this.pointClass) .attr('cx', d => { return x(new Date(item.created_at)); }) .transition() .duration(Math.floor(Math.random() * (3000-2000) + 1000)) .ease(d3.easeBounce) .attr('cy', d => { let today = new Date(); let time = new Date(item.created_at); return y(today.setHours(time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds())); }) .attr('r', 5); }); }
}

We can now make a call for some data and render this chart…

// get the data
let data = fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/4548/time-series.json').then(d => d.json()).then(data => { // massage the data a bit to get it in the right format let horseData = data.map(item => { return item.horse; }) // create the chart let chart = new ScatterPlot('#chart', { data: horseData, width: 960 });
});

And here is the whole thing, complete with a call to our Azure Function returning the data from Cosmos DB. It’s a TON of data, so be patient while we chew up all your bandwidth.

See the Pen GYvGep by Burke Holland (@burkeholland) on CodePen.

If you made it this far, I…well, I’m impressed. D3 is not an easy thing to get into. It simply doesn’t look like it’s going to be any fun. BUT, no thumbs were smashed here, and we now have complete control of this chart. We can do anything we like with it.

Check out some of these additional resources for D3, and good luck with your chart. You can do it! Or you can’t. Either way, someone has to make a chart, and it might as well be you.

For your data and API:

More on D3:

The post Hand roll charts with D3 like you actually know what you’re doing appeared first on CSS-Tricks.

How to make a modern dashboard with NVD3.js

NVD3.js is a JavaScript visualization library that is free to use and open source. It’s derived from the well-known d3.js visualization library. When used the right way, this library can be extremely powerful for everyday tasks and even business operations.

For example, an online dashboard. We can use NVD3.js to compile data into a centralized space that visualizes the information in neat charts and graphs. That’s what we’re going to look at in this post.

Making a dashboard with NVD3.js for the first time is daunting, but after this tutorial, you should have the required knowledge to get your hands dirty and start building something awesome. Personally, I have a passion for visualizations on the web. They are both beautiful and meaningful at the same time.

Real-world use case: A data dashboard

Dashboards can be used for pretty much anything. As long as you’ve got data to analyze, you’re good to go, whether that be some sales data or the average weather for the last 20 years. Let’s build something like this:

See the Pen Dashboard NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Setting up the environment

To setup an NVD3 chart, we require three things:

  1. The NVD3 JavaScript library
  2. The NVD3 CSS library
  3. The D3.js library (a dependency for NVD3)

All of these are available through Cloudflare’s CDN network. Here’s an HTML template with all those resources ready to go:

<!doctype html>
<html lang="en">
<head> <meta charset="utf-8"> <title>Making a Dashboard</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.css">
</head>
<body> </body>
</html>

Data sources

For this tutorial, I thought using some raw, factual data that’s already been formatted into JSON would be easiest. I’m using two sources of information: one about global average temperatures over time and the other reporting world population statistics. I’ve already formatted the data into JSON, so it’s ready to copy and paste!

Creating a line chart

Let’s go ahead and create a line chart with some global temperature data. The raw data I’ve put together in JSON represents the last twenty years of temperature changes compared to the global average.

First, we’ll add a placeholder element for the chart to attach to when the JavaScript is executed.

<div id="averageDegreesLineChart" class='with-3d-shadow with-transitions averageDegreesLineChart'> <svg></svg>
</div>

Then add the following JSON in some script tags before the </body> tag:

var temperatureIndexJSON = [ { key: "Temp +- Avg.", values: [{ "x": 1998, "y": 0.45 }, { "x": 1999, "y": 0.48 }, { "x": 2000, "y": 0.5 }, { "x": 2001, "y": 0.52 }, { "x": 2002, "y": 0.55 }, { "x": 2003, "y": 0.58 }, { "x": 2004, "y": 0.6 }, { "x": 2005, "y": 0.61 }, { "x": 2006, "y": 0.61 }, { "x": 2007, "y": 0.61 }, { "x": 2008, "y": 0.62 }, { "x": 2009, "y": 0.62 }, { "x": 2010, "y": 0.62 }, { "x": 2011, "y": 0.63 }, { "x": 2012, "y": 0.67 }, { "x": 2013, "y": 0.71 }, { "x": 2014, "y": 0.77 }, { "x": 2015, "y": 0.83 }, { "x": 2016, "y": 0.89 }, { "x": 2017, "y": 0.95 }] }
];

The x value is the year and they value is the temperature in Celsius degrees.

The last piece to the puzzle is the most important function that creates the chart. The function named nv.addGraph() is the main function used throughout NVD3 and, within it, you initialize the chart object. In this example, we are using the lineChart object which can have methods chained to it, depending on what the visual requirements may be.

Check out the JavaScript comments to see the which each line does.

nv.addGraph(function () { var chart = nv.models.lineChart() // Initialise the lineChart object. .useInteractiveGuideline(true); // Turn on interactive guideline (tooltips) chart.xAxis .axisLabel('TimeStamp (Year)'); // Set the label of the xAxis (Vertical)
chart.yAxis .axisLabel('Degrees (c)') // Set the label of the xAxis (Horizontal) .tickFormat(d3.format('.02f')); // Rounded Numbers Format.
d3.select('#averageDegreesLineChart svg') // Select the ID of the html element we defined earlier. .datum(temperatureIndexJSON) // Pass in the JSON .transition().duration(500) // Set transition speed .call(chart); // Call & Render the chart nv.utils.windowResize(chart.update); // Intitiate listener for window resize so the chart responds and changes width. return;
});

See the Pen Line Chart NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Creating two bar charts for the price of one

You will love the minimal effort of these next two charts. The power of NVD3.js enables switching between chart types. You can either have the toggle active so users can switch between a standard bar chart or a stacked bar chart. Or you can force the chart type and make it unchangeable.

The following examples show exactly how to do it.

Stacked multi-bar chart

You know those bar charts that stack two values together in the same bar? That’s what we’re doing here.

<div id="worldPopulationMultiBar" class="with-3d-shadow with-transitions worldPopulationMultiBar"> <svg></svg>
</div>
var populationBySexAndCountryJSON =[{"key":"Male","color":"#d62728","values":[{"label":"China","value":717723466.166},{"label":"India","value":647356171.132},{"label":"United States of America","value":157464952.272},{"label":"Indonesia","value":125682412.393},{"label":"Brazil","value":98578067.1},{"label":"Pakistan","value":93621293.316},{"label":"Nigeria","value":88370210.605},{"label":"Bangladesh","value":79237050.772},{"label":"Russian Federation","value":65846330.629},{"label":"Japan","value":61918921.999}]},{"key":"Female","color":"#1f77b4","values":[{"label":"China","value":667843070.834},{"label":"India","value":604783424.868},{"label":"United States of America","value":162585763.728},{"label":"Indonesia","value":124183218.607},{"label":"Brazil","value":101783857.9},{"label":"Pakistan","value":88521300.684},{"label":"Nigeria","value":85245134.395},{"label":"Bangladesh","value":77357911.228},{"label":"Russian Federation","value":76987358.371},{"label":"Japan","value":65224655.001}]}]; nv.addGraph(function ()
{ var chart = nv.models.multiBarChart() .x(function (d) { return d.label; // Configure x axis to use the "label" within the json. }) .y(function (d) { return d.value; // Configure y axis to use the "value" within the json. }).margin({top: 30, right: 20, bottom: 50, left: 85}) // Add some CSS Margin to the chart. .showControls(false) // Turn of switchable control .stacked(true); // Force stacked mode. chart.xAxis.axisLabel('Countries'); // add label to the horizontal axis chart.yAxis.tickFormat(d3.format('0f')); // Round the yAxis values d3.select('#worldPopulationMultiBar svg') // Select the html element by ID .datum(populationBySexAndCountryJSON) // Pass in the data .transition().duration(500) // Set transition speed .call(chart); // Call & Render chart nv.utils.windowResize(chart.update); // Intitiate listener for window resize so the chart responds and changes width. return;
});

The two important settings in the JavaScript here are the .showControls and .stacked booleans. They both do what they say on the tin: force the graph to a stacked bar chart and don’t allow switching of the chart type. You will see what I mean by switching soon.

See the Pen Multi StackedBar NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Standard multi-bar chart

Now, let’s do something a little more traditional and compare values side-by-side instead of stacking them in the same bar.

This uses pretty much the same code as the stacked examples, but we can change the .stacked boolean value to false. This will, of course, make the stacked bar chart a normal bar chart.

<div id="worldPopulationMultiBar" class="with-3d-shadow with-transitions worldPopulationMultiBar"> <svg></svg>
</div>
var populationBySexAndCountryJSON = [{"key":"Male","color":"#d62728","values":[{"label":"China","value":717723466.166},{"label":"India","value":647356171.132},{"label":"United States of America","value":157464952.272},{"label":"Indonesia","value":125682412.393},{"label":"Brazil","value":98578067.1},{"label":"Pakistan","value":93621293.316},{"label":"Nigeria","value":88370210.605},{"label":"Bangladesh","value":79237050.772},{"label":"Russian Federation","value":65846330.629},{"label":"Japan","value":61918921.999}]},{"key":"Female","color":"#1f77b4","values":[{"label":"China","value":667843070.834},{"label":"India","value":604783424.868},{"label":"United States of America","value":162585763.728},{"label":"Indonesia","value":124183218.607},{"label":"Brazil","value":101783857.9},{"label":"Pakistan","value":88521300.684},{"label":"Nigeria","value":85245134.395},{"label":"Bangladesh","value":77357911.228},{"label":"Russian Federation","value":76987358.371},{"label":"Japan","value":65224655.001}]}]; nv.addGraph(function () { var chart = nv.models.multiBarChart() .x(function (d) { return d.label; // Configure x axis to use the "label" within the json. }) .y(function (d) { return d.value; // Configure y axis to use the "value" within the json. }).margin({top: 30, right: 20, bottom: 50, left: 85}) // Add some CSS Margin to the chart. .showControls(false) // Turn of switchable control .stacked(false); // ***Force normal bar mode.*** chart.xAxis.axisLabel('Countries'); // add label to the horizontal axis chart.yAxis.tickFormat(d3.format('0f')); // Round the yAxis values d3.select('#worldPopulationMultiBar svg') // Select the html element by ID .datum(populationBySexAndCountryJSON) // Pass in the data .transition().duration(500) // Set transition speed .call(chart); // Call & Render chart nv.utils.windowResize(chart.update); // Intitiate listener for window resize so the chart responds and changes width. return;
});

The one change to the settings presents us with a brand-new looking chart.

See the Pen Multi Bar NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Using one code set with minimal changes, you just created two epic charts for the price of one. Kudos for you!

There is one last setting, of course, if you want the functionality of switching charts. Change the .showControls to true and remove the .stacked option. You will notice some controls at the top of the chart. Clicking them will toggle the view between a stacked and standard chart.

See the Pen Multi Bar Switchable NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Making a dashboard

There’s nothing I love looking at more in the world of web development than dashboards. They never get old and are super good looking. By using the charting techniques we’ve already covered, we can make our own responsive dashboard on a single webpage.

If you remember within the JavaScript snippets earlier, there was a function set with a listener against each chart: nv.utils.windowResize(chart.update);

This magical function, resizes the chart for you as if it was set to width: 100% in CSS. But it doesn’t just shrink, but also moves and restructures the graph according to the size of the screen. It’s pretty awesome! All we need to worry about it are the heights. We can set this up by applying flexbox to the classes assigned to chart elements.

Let’s bundle everything we have so far into one dashboard by wrapping each chart element in a flexbox container. Apply a small amount of CSS for the flexing and height, and finally, compile all the scripts into one (or, you can keep them separate in your own project).

See the Pen Dashboard NVD3.JS by Danny Englishby (@DanEnglishby) on CodePen.

Summary

This tutorial will hopefully give you the knowledge to get started with building data visualizations and, ultimately, add them into a production dashboard. Even if you only want to play with these concepts, I’m sure your mind will start running with ideas for how to transform different types of data into stunning visuals.

The post How to make a modern dashboard with NVD3.js appeared first on CSS-Tricks.

Observable

Observable launched a couple of weeks ago. As far as I understand, it’s sort of like a mix between CodePen and Medium where you create “notebooks” for exploring data, making nifty visualizations.

Check out this a note about this interesting new format, founder Mike Bostock describes a notebook as “an interactive, editable document defined by code. It’s a computer program, but one that’s designed to be easier to read and write by humans.”

All of this stuff riffs on a lot of Mike’s previous work which is definitely worth exploring further if you’re a fan of complex visualizations on the web.

Direct Link to Article — Permalink


Observable is a post from CSS-Tricks