Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
D3.js 4.x Data Visualization - Third Edition
D3.js 4.x Data Visualization - Third Edition

D3.js 4.x Data Visualization: Learn to visualize your data with JavaScript, Third Edition

By Aendrew Rininsland , Swizec Teller
$15.99 per month
Book Apr 2017 308 pages 3rd Edition
eBook
$35.99 $24.99
Print
$43.99
Subscription
$15.99 Monthly
eBook
$35.99 $24.99
Print
$43.99
Subscription
$15.99 Monthly

What do you get with a Packt Subscription?

Free for first 7 days. $15.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details


Publication date : Apr 28, 2017
Length 308 pages
Edition : 3rd Edition
Language : English
ISBN-13 : 9781787120358
Category :
Table of content icon View table of contents Preview book icon Preview Book

D3.js 4.x Data Visualization - Third Edition

Chapter 1. Getting Started with D3, ES2017, and Node.js

Data-Driven Documents (D3), developed by Mike Bostock and the D3 community in 2011, is the successor to Bostock's earlier Protovis library. It allows pixel-perfect rendering of data by abstracting the calculation of things such as scales and axes into an easy-to-use Domain-Specific Language (DSL), and uses idioms that should be immediately familiar to anyone with experience of using the popular jQuery JavaScript library. Much like jQuery, in D3, you operate on elements by selecting and then manipulating them via a chain of modifier functions. Especially within the context of data visualization, this declarative approach makes using it easier and more enjoyable than a lot of other tools out there. The official website, https://d3js.org/, features many great examples that show off the power of D3, but understanding them is tricky to start with. After finishing with this book, you should be able to understand D3 well enough to figure out the examples, tweaking them to fit your needs. If you want to follow the development of D3 more closely, check out the source code hosted on GitHub at https://github.com/d3.

In this chapter, we'll lay the foundations of what you'll need to run all the examples in the book. I'll explain how you can start writing ECMAScript 2017 (ES2017)--the latest and most advanced version of JavaScript--and show you how to use Babel to transpile it to ES5, allowing your modern JavaScript to be run on any browser. We'll then cover the basics of using D3 v4 to render a basic chart.

What is D3.js?


The fine-grained control and its elegance make D3 one of the most powerful open source visualization libraries out there. This also means that it's not very suitable for simple jobs, such as drawing a line chart or two--in that case, you may want to use a library designed for charting. Many use D3 internally anyway. For a massive list, visit https://github.com/sorrycc/awesome-javascript#data-visualization.

D3 is ultimately based around functional programming principles, which is currently experiencing a renaissance in the JavaScript community. This book isn't really about functional programming, but a lot of what we'll do will seem really familiar if you've ever used functional programming principles before. If you haven't, or come from an Object-Oriented (OO) background like I do, don't worry, I'll explain the important bits as we get to them, and the revised section on functional programming at the beginning of Chapter 4, Making Data Useful, will hopefully give you some insight into why this paradigm is so useful, especially for data visualization and application construction.

What happened to all the classes?

The second edition of this book contained quite a number of examples using the class feature that is new in ES2015. The revised examples in this edition use factory functions instead, and the class keyword never appears. Why is this, exactly?

ES2015 classes are essentially just syntactic sugaring for factory functions. By this I mean that they ultimately transpile down to factory functions anyway. Although classes can provide a certain level of organization to a complex piece of code, they ultimately hide what is going on underneath it all. Not only that, using OO paradigms, such as classes, is effectively avoiding one of the most powerful and elegant aspects of JavaScript as a language, which is its focus on first-class functions and objects. Your code will be simpler and more elegant using functional paradigms than OO, and you'll find it less difficult to read examples in the D3 community, which almost never use classes.

There are many, much more comprehensive arguments against using classes than I'm able to make here. For one of the best, refer to Eric Elliott's excellent The Two Pillars of JavaScript pieces at: www.medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3.

What's new in D3 v4?


One of the key changes to D3 since the last edition of this book is the release of version 4.

Among its many changes, the most significant one is a complete overhaul of the D3 namespace. This means that none of the examples in this book will work with D3 3.x, and the examples from Learning D3.js Data Visualization, Second Edition will not work with D3 4.x. This is quite possibly the cruelest thing Mr. Bostock could ever do to educational authors such as myself (I am joking here!). Kidding aside, it also means many of the block examples in the D3 community are out of date and may appear rather odd if this book is your first encounter with the library. For this reason, it is very important to note the version of D3 an example uses -- if it uses 3.x, it might be worth searching for a 4.x example just to prevent this cognitive dissonance.

Note

You can usually find the version of D3 in an example block online by looking at the script tags near the top of the code. If it resembles: <script src="https://d3js.org/d3.v3.min.js"></script>...You're looking at a D3 v3 example. If it says:<script src="https://d3js.org/d3.v4.min.js"></script>...You're looking at a modern, v4 example and are good to go.

Related to this is how D3 has been broken up from a single library into many smaller libraries (or micro-libraries). There are two approaches you can take:

  • You can use D3 as a single library (a monolib) in much the same way as version 3
  • You can selectively use individual (microlib) components of D3 in your project

This book takes the former route. While learning D3, using micro-libraries takes a lot more effort, even if it helps reduce the size of the final bundle that people who view your graphics will have to download. That said, I will try to signpost which package a particular piece of functionality resides in; so, once you become more comfortable with D3, you can start using the microlibs instead of including everything and the kitchen sink.

What's ES2017?


One of the main changes to this book since its first edition is the emphasis on modern JavaScript; in this case, ES2017. Formerly known as ES6 (Harmony), it pushes the language features of JavaScript forward significantly, allowing for new usage patterns that simplify code readability and increased expressiveness. If you've written JavaScript before and the examples in this chapter look pretty confusing, it means you're probably familiar with the older, more common ES5 syntax.However, don't sweat! It really doesn't take too long to get the hang of the new syntax, and I will try to explain the new language features as we encounter them. Although it might seem to be something of a steep learning curve at the start, by the end, you'll have improved your ability to write code quite substantially and will be on the cutting edge of contemporary JavaScript development.

Note

For a really good rundown of all the new toys you have with ES2015-17, check out  https://babeljs.io/docs/learn-es2015/, a nice guide by the folks at Babel.js, which we will use extensively throughout this book. 

Before I go any further, let me clear some confusion about what ES2017 actually is. Initially, the ECMAScript (or ES for short) standards were incremented by cardinal numbers, for instance, ES4, ES5, ES6, and ES7. However, with ES6, they changed this so that a new standard is released every year in order to keep pace with modern development trends, and thus we refer to the year (2017) now. The big release was ES2015, which more or less maps to ES6. ES2016 was ratified in June 2016, and builds on the previous year's standard, while adding a few fixes and two new features. ES2017 is currently in the draft stage, which means proposals for new features are being considered and developed until it is ratified sometime in 2017. As a result of this book being written while these features were in draft, they may not actually make it to ES2017 and thus may need to wait until a later standard to be officially added to the language.

You don't really need to worry about any of this, however, because we use Babel.js to transpile everything down to ES5 anyway, so it runs the same in Node.js and in the browser. I will try to refer to the relevant specification where a feature is added, when I introduce it for the sake of accuracy (for instance, modules are an ES2015 feature), but when I refer to JavaScript, I mean all modern JavaScript, regardless of which ECMAScript specification it originated in.

Getting started with Node and Git on the command line


I will try not to be too opinionated in this book about which editor or operating system you should use to work through it (though I am using Atom on macOS X), but you will need a few prerequisites to start.

The first is Node.js. Node is widely used for Web development nowadays, and it's actually just JavaScript that can be run on the command line. Later on, in this book, I'll show you how to write a server application in Node, but for now, let's just concentrate on getting it and npm (the brilliant and amazing package manager that Node uses) installed. If you're on Windows or macOS X without Homebrew, use the installer at https://nodejs.org/en/. If you're on macOS X and are using Homebrew, I would recommend installing n instead, which allows you to easily switch between versions of Node:

$ brew install n
$ n lts

Note

If you're in Windows, the $ above might be confusing. In UNIX-based operating systems, regular users see a $ on the command prompt, and the root administrator user sees a #. By indicating that, I mean that you should run the above commands as a regular user and not a super-user.

Regardless of how you do it, when you have finished, verify by running the following lines:

$ node --version
$ npm --version

If it displays the versions of node and npm, it means you're good to go.

Note

I'm using 6.5.0 and 3.10.3, respectively, though yours might be slightly different--the key is making sure that node is at least version 6.0.0.

If it says something similar to Command not found, double-check whether you've installed everything correctly, and verify that Node.js is in your $PATH environment variable.

Throughout this book, we will use a combination of Babel and Webpack to turn our fancy modular modern JavaScript into something even the crummiest old browsers (Hi, Internet Explorer 9!) can run.

First, create a package.json file to store the version of each dependency that we want. Do this by first creating a new folder and then running npm init:

$ mkdir d3-projects
$ cd d3-projects
$ npm init -y

The -y flag tells npm init to use all the default settings and not ask you any questions.  Next, install our stack, using npm:

$ npm install "babel-core@^6" "babel-loader@^6" "babel-preset-es2017@^6" "babel-preset-stage-0@^6" "webpack@^2" "webpack-dev-server@^2" css-loader style-loader json-loader --save-dev

This installs v2 of Webpack, v6 of Babel, and a boatload of presets and plugins for both. It then saves these to package.json so that you can reinstall them as easily as running:

$ npm install

You'll also need to install D3 by typing the following command:

$ npm install d3 --save

Next, we need to create a configuration for Webpack. I won't get into detail around what each of the directives do; for that, look at the config supplied with the book repository; save this as webpack.config.js:

const path = require('path');
module.exports = [{
  entry: {
    app: ['./lib/main.js'],
  },
  output: {
    path: path.resolve(__dirname, 'build'),
    publicPath: '/assets/',
    filename: 'bundle.js',
  },
  devtool: 'inline-source-map',
  module: {
    rules: [{
      test: /.js?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel-loader',
    }, {
      test: /.json$/,
      loader: 'json-loader',
    }, {
      test: /.css$/,
      loader: 'style-loader!css-loader',
    }],
  },
}];

 

Lastly, we need to edit package.json to have a few shortcuts to make our lives easier. After the line that starts with name, put the following:

"babel": {
  "presets": [
    "es2017"
  ]
},
"main": "lib/main.js",
"scripts": {
  "start": "webpack-dev-server --inline",
},

This is what you need to do if you're starting a project from scratch.

Alternatively, you can clone the book's repository from GitHub. GitHub is where most of the world stores open source and other code. I've done a lot of configuration for you, in addition to supplying all of the examples and sample data. I'll move forward under the assumption you've cloned the book repository and are working out of the book repository directory. To do this, run the following command:

$ git clone https://github.com/aendrew/learning-d3-v4
$ cd learning-d3-v4

This will clone the development environment and all the samples in the learning-d3-v4/ directory, as well as switching you into it and installing all of the dependencies via npm.

Note

Another option is to fork the repository on GitHub and then clone your fork instead of mine as was just shown in the preceding code. This will allow you to easily publish your work on the cloud, enabling you to more readily seek support, display finished projects on GitHub Pages, and even submit suggestions and amendments to the parent project. This will help us improve this book for future editions. To do this, fork aendrew/learning-d3-v4 by clicking the "fork" button in GitHub, and replace aendrew in the preceding code snippet with your GitHub username.

Each chapter of this book is in a separate branch. To switch between them, type the following command:

$ git checkout chapter1

 

Replace 1 with whichever chapter you want the examples for. Stay on the master branch for now, though. To get back to it, type this line:

$ git stash save && git checkout master

The master branch is where you'll do a lot of your coding as you work through this book.  We still need to install our dependencies, so let's do that now:

$ npm install

All of the source code that you'll be working on is in the lib/ folder. You'll notice it contains just a main.js file; almost always, we'll be working in main.js, as index.html is just a minimal container to display our work in. This is it in its entirety, and it's the last time we'll look at any HTML in this book:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <title>Learning Data Visualization with D3.js</title>
 </head>
 <body>
 <script src="assets/bundle.js"></script>
 </body>
</html>

There's also an empty style sheet in styles/index.css, which we'll add to in a bit.

Next, start the development server by typing the following line:

$ npm start

This starts up the Webpack development server, which will transform our new-fangled ES2017 JavaScript into backward-compatible ES5 and serve it to the browser. Now, point Chrome (or whatever, I'm not fussy--so long as it's not Internet Explorer!) to localhost:8080 and fire up the developer console (CtrlShift + J for Linux and Windows and Option + Command + J for Mac). You should see a blank website and a blank JavaScript console with a Command Prompt waiting for some code:

A quick Chrome Developer Tools primer


Chrome Developer Tools are indispensable to Web development. Most modern browsers have something similar, but to keep this book shorter, we'll stick to just Chrome here for the sake of simplicity. Feel free to use a different browser. Firefox's Developer Edition is particularly nice, and--yeah yeah, I hear you guys at the back--Opera is good too! We will mostly use the Elements and Console tabs, Elements to inspect the DOM and Console to play with JavaScript code and look for any problems. The other six tabs come in handy for large projects:

The Network tab will let you know how long files will take to load and help you inspect the Ajax requests. The Profiles tab will help you profile JavaScript for performance. The Resources tab is good for inspecting client-side data. Timeline and Audits are useful when you have a global variable that is leaking memory and you're trying to work out exactly why your library is suddenly causing Chrome to use 500 MB of RAM. While I've used these in D3 development, they're probably more useful when building large Web applications with frameworks such as React and Angular.

The main one you want to focus on, however, is Sources, which shows all the source code files that have been pulled in by the web page. Not only is this useful in determining whether your code is actually loading, it also contains a fully functional JavaScript debugger, which few mortals dare to use. Although explaining how to debug code is kind of boring and not at the level of this chapter, learning to use breakpoints instead of perpetually using console.log to figure out what your code is doing, is a skill that will take you far in the years to come. For a good overview, visit https://developers.google.com/web/tools/chrome-devtools/debug/breakpoints/step-code?hl=en Most of what you'll do with Developer Tools, however, involves looking at the CSS inspector at the right-hand side of the Elements tab. It can tell you what CSS rules are impacting the styling of an element, which is very good for hunting rogue rules that are messing things up. You can also edit the CSS and immediately see the results, as follows:

The obligatory bar chart example


No introductory chapter on D3 would be complete without a basic bar chart example. They are to D3 as Hello World is to everything else, and 90% of all data storytelling can be done in its simplest form with an intelligent bar or line chart. For a good example of this, look at the kinds of graphics the Financial Times or The Economist includes in their articles--they frequently summarize the entire piece with a simple line chart or histogram. Coming from a newsroom development background (Full disclosure: I work on the Interactive Graphics desk at the Financial Times), many of my examples will be related to some degree to current events or possible topics worth visualizing with data. The news development community has been really instrumental in creating the environment for D3 to flourish, and it's increasingly important for aspiring journalists to have proficiency in tools such as D3. The first dataset that we'll use is the UK Electoral Commission's result set from the UK Brexit referendum. We will draw a bar chart depicting voter turnout by region.

The source for this data is at http://www.electoralcommission.org.uk/find-information-by-subject/elections-and-referendums/past-elections-and-referendums/eu-referendum/electorate-and-count-information.

We'll create a bar for each region in the UK. The first step is to get a basic container setup, which we can then be populated with all of our delicious new JavaScript code. We can either jump straight into the code, or we can set up some stuff to make life easier for us later on. Let's go with the first route for now. There's a lot of new-fangled JavaScript stuff coming at you really soon, so let's keep it light for the moment.

First open lib/main.js and write your very first line of D3:

const chart = d3.select('body') 
.append('svg') 
.attr('id', 'chart');

This selects the HTML <body> element, appends a <svg> element, and gives it the ID of #chart. We'll be using this pattern a lot throughout the book.

Note

Before we get any further, it's worth pointing out our first new-fangled modern JavaScript feature: The const keyword is used to define a variable that won't change dramatically. By dramatically I mean that you can still modify it somewhat (for instance, adding elements to an array or modifying an object), but you'll throw an error if you try to reassign it. It will also throw an error if you try to use it before it's declared. Unlike constants in other languages, JavaScript constants only apply to the current function scope (they're not global unless you make them that). This is really useful when using a functional programming style, as it prevents weird bugs caused by variable hoisting (an unusual JavaScript language feature, whereby variables are ultimately interpreted at the top of each function closure instead of where you actually define them). For more on const, visit http://mdn.io/const.Another new way to define variables in JavaScript is using let, which is like var but, like const, has block scope, meaning that it is limited to the block, statement, or expression where it's used. This also helps prevent weird bugs. For more information, visit http://mdn.io/let.When should you use each? Use const if you're not going to reassign a variable and use let if you will. I try to avoid reassigning variables, so I will usually use const in this book. However, while you still can use the var keyword to assign variables in modern JavaScript, you really shouldn't--always use let or const instead, defaulting to let if you're not sure which will work in a given situation.

Yeah! Let's open this up in the browser; ensure that the development server is running (npm start if it isn't) and go to http://localhost:8080

Uncaught Error: Cannot find module "d3"

Whups. Okay, that could have gone better...

You're getting this error because we haven't imported D3, yet. If you've used D3 before, you might be more familiar with it attached to the window global object. This is what happens when you include d3.js via a <script> tag. We're not doing that, however--we're JavaScript rockstarninjaciraptors; we use the new hotness, ES2015 module imports!

Go back to main.js. At the top of the file, type this:

import * as d3 from 'd3'; 

Let's unpack this a bit. Import statements must be at the top of every file (so no sneaky Node.js-style require() calls inside your functions), because it allows for static analysis. This lets new JavaScript tools be more effective. They always start with the import token.

Next is the curly bracket bit. In an ES2015 module, there are two types of exports:

  • Named: This is where you give the export a title that needs to be imported specifically (though it can be renamed), and it is inside curly brackets. 
  • Default: There can be only one of these per module, and it can be referred to as anything when importing. We'll see this a bit later on.

What we do above is import all of the D3 microlibs under the namespace d3.

If you go back to your browser and switch quickly to the Elements tab, you'll notice a new SVG with an ID of #chart at the bottom of the page. There's progress!

Loading in data

Go back to main.js. We need to get our data in somehow, and I'll show you far better ways of doing this later on, but let's work through the pain and do this the bad, old way--using XMLHttpRequest:

const req = new window.XMLHttpRequest(); 
req.addEventListener('load', mungeData); 
req.open('GET', 'data/EU-referendum-result-data.csv'); 
req.send(); 

This instantiates a new XMLHttpRequest object, tells it to load the data from the data/directory and then passes it to the soon-to-be-written mungeData() function once loaded.

Note how we had to use the ugly new keyword to instantiate it? Note how it took four lines and a new variable declaration? Note how we have to handle our response in a callback? Eww! We'll improve upon this in later chapters. The only advantage of doing things this way is that it works in nearly any browser without polyfilling, but there are so many better ways of doing this, all of which we will touch upon in Chapter 4, Making Data Useful.

The CSV file we're loading in has a row for each constituency in the UK, containing everything from what percentage voted for what to what the voter turnout was to how many ballots were invalid or spoiled. What we will do is turn that into an array of objects depicting the mean percentage for each broader region that voted for leaving.

It's time to create our mungeData() function. We will use d3.csvParse() (from the d3-dsv microlib) to parse our CSV data string in an object and then use some features from the d3-array microlib to manipulate that data:

function mungeData() { 
  const data = d3.csvParse(this.responseText); 
  const regions = data.reduce((last, row) => { 
    if (!last[row.Region]) last[row.Region] = [];
    last[row.Region].push(row); 
    return last; 
  }, {}); 
  const regionsPctTurnout = Object.entries(regions) 
    .map(([region, areas]) => ({ 
      region, 
      meanPctTurnout: d3.mean(areas, d => d.Pct_Turnout), 
    })); 

  renderChart(regionsPctTurnout); 
} 

Note

Hey, there's another ES2015 feature! Instead of typing function() {} endlessly, you can now just put () => {} for anonymous functions. Other than being six keystrokes less, the fat arrow doesn't bind the value of this to something else. This won't impact us very much because we're using a functional style of programming; but if we were using classes, this would be a lifesaver. For more on this, visit http://mdn.io/Arrow_functions.

We transform our data in three steps here:

  1. First, we convert it into an array of objects using d3.csvParse() and assign the result to data.
  2. Then, we transform the array into an object keyed by the region, such that the object's keys are the regions, and the values are an array of associated constituencies.
  3. Lastly, Object.entries converts an object into a multidimensional array consisting of elements comprising key-value pairs, which we can then reduce into a new object comprising each region's name and the mean of each constituency's voter turnout percentage.

You may have noted that the function signature for the call to Array.prototype.map is a little unusual:

.map(([region, areas]) => { 

Here, we use a new ES2015 feature called destructuring assignment to give each element in our array a temporary name. Normally, the callback signature is the following:

function(item, index, array) {} 

However, because we know item is an array with two elements, we can give each of them a nickname, making our code easier to read (we don't use the index or array arguments this particular time, but if we did, we'd just put those arguments after the destructuring bit).

Lastly, we pass our fully munged data to an as-of-yet-unwritten function, renderChart(), which we'll add next.

We can also simply add the above to this:

  const regionsPctTurnout = d3.nest() 
    .key(d => d.Region) 
    .rollup(d => d3.mean(d, leaf => leaf.Pct_Turnout)) 
    .entries(data); 

d3.nest() is part of the d3-collection microlib, which we'll cover in--you guessed it--Chapter 4, Making Data Useful. D3 is a very un-opinionated library, which means you can accomplish many tasks in a variety of ways--there often really isn't a proper way of doing things. I will try to expose a variety of ways to accomplish tasks throughout the book; feel free to choose whichever you prefer.

Twelve (give or take a few) bar blues

With that done, let's render some data.

Create a new function in main.js, renderChart():

function renderChart(data) { 
  chart.attr('width', window.innerWidth) 
    .attr('height', window.innerHeight); 
} 

All this does is take our earlier chart variable and set its width and height to that of the window. We're almost at the point of getting some bars onto that graph; hold tight!

First, however, we need to define our scales, which decide how D3 maps data values to pixel values. Put another way, a scale is simply a function that maps an input range to an output domain. This can be annoying to remember, so I'm going to shamelessly steal an exercise from Scott Murray's excellent tutorial on scales from Interactive Data Visualization for the Web:

When I say "input," you say "domain." Then I say "output," and you say "range." Ready? Okay: Input! Domain! Output! Range! Input! Domain! Output! Range! Got it? Great.

It seems silly, but I frequently find myself muttering the above when I have a deadline and am working on a chart late at night. Give it a go!

Next, add this code to renderChart():

const x = d3.scaleBand()
  .domain(data.map(d => d.region))
  .rangeRound([50, window.innerWidth - 50])
  .padding(0.1);

The x scale is now a function that maps inputs from a domain composed of our region names to a range of values between 50 and the width of your viewport (minus 50), with some spacing defined by the 0.1 value given to .padding(). What we've created is a band scale, which is like an ordinal scale, but the output is divided into sections. We'll talk more about scales later on in the book.

Note

In this example, we use a uniform value of 50 for our margins, which we pass to our scales and elsewhere. Any arbitrary number passed in code is often referred to as a magic number, insomuch that, to anyone reading your code, it just looks like a random value that magically makes it work. This is bad; don't do this--it makes your code harder to read, and it means that you have to find and replace every value if you want to change it. I only do so here to demonstrate this fact. Throughout the rest of the book, we'll define things, such as margins more intelligently; stay tuned!

Still inside renderChart(), we define another scale named y:

const y = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.meanPctTurnout)])
  .range([window.innerHeight - 50, 0]);

Similarly, the y scale is going to map a linear domain (which runs from zero to the max value of our data, the latter of which we acquire using d3.max) to a range between window.innerHeight (minus our 50 pixel margin) and 0. Inverting the range is important because D3 considers the top of a graph to be y=0. If ever you find yourself trying to troubleshoot why a D3 chart is upside down, try switching the range values in one of your scales.

Now, we define our axes. Add this just after the preceding line, inside renderChart:

const xAxis = d3.axisBottom().scale(x); 
const yAxis = d3.axisLeft().scale(y); 

 

We've told each axis what scale to use when placing ticks and which side of the axis to put the labels on. D3 will automatically decide how many ticks to display, where they should go, and how to label them. Since most D3 elements are objects and functions at the same time, we can change the internal state of both scales without assigning the result to anything. The domain of x is a list of discrete values. The domain of y is a range from 0 to the d3.max of our dataset, the largest value.

Now, we will draw the axes on our graph:

chart.append('g')
  .attr('class', 'axis')
  .attr('transform', 
    &grave;translate(0, ${window.innerHeight - 50})&grave;)
  .call(xAxis);

Note

Hot new ES2015 feature alert! Above, the transform argument is in backticks (&grave;), which are template literal strings. They're just like normal strings, except for two differences: you can use newline characters in them, and you can also run arbitrary JavaScript expressions in them via the ${} syntax. Above, we merely echo out the value of window.innerHeight, but you can write any expression that returns a string-like value, for instance, using Array.prototype.join to output the contents of an array; it's really handy!

We've appended an element called g to the graph, given it the axis CSS class, and moved the element to a place in the bottom-left corner of the graph with the transform attribute.

Finally, we call the xAxis function and let D3 handle the rest.

The drawing of the other axis works exactly the same, but with different arguments:

  chart.append('g') 
    .attr('class', 'axis') 
    .attr('transform', 'translate(50, 0)') 
    .call(yAxis); 

 

Now that our graph is labeled, it's finally time to draw some data:

  chart.selectAll('rect') 
    .data(data) 
    .enter() 
    .append('rect') 
    .attr('class', 'bar') 
    .attr('x', d => x(d.region)) 
    .attr('y', d => y(d.meanPctTurnout)) 
    .attr('width', x.bandwidth()) 
    .attr('height', d =>
        (window.innerHeight - 50) - y(d.meanPctTurnout));

Okay, there's plenty going on here, but this code is saying something very simple. This is what is says:

  • For all rectangles (rect) in the graph, load our data
  • Go through it
  • For each item, append a rect
  • Then, define some attributes to it

Note

Ignore the fact that there aren't any rectangles initially; what you're doing is creating a selection that is bound to data and then operating on it. I can understand that it feels a bit weird to operate on nonexistent elements (this was personally one of my biggest stumbling blocks when I was learning D3), but it's an idiom that shows its usefulness later on when we start adding and removing elements due to changing data.

The x scale helps us calculate the horizontal positions, and bandwidth() gives the width of the bar. The y scale calculates vertical positions, and we manually get the height of each bar from y to the bottom. Note that whenever we needed a different value for every element, we defined an attribute as a function (x, y, and height); otherwise, we defined it as a value (width).

Let's add some flourish and make each bar grow out of the horizontal axis. Time to dip our toes into animations! Modify the code you just added to resemble the following; I've highlighted the lines that are different:

chart.selectAll('rect') 
  .data(data) 
  .enter() 
  .append('rect') 
  .attr('class', 'bar') 
  .attr('x', d => x(d.region)) 
.attr('y', window.innerHeight - 50)
  .attr('width', x.bandwidth()) 
  .attr('height', 0) 
    .transition() 
    .delay((d, i) => i * 20) 
    .duration(800) 
    .attr('y', d => y(d.meanPctTurnout)) 
    .attr('height', d =>
        (window.innerHeight - 50) - y(d.meanPctTurnout));

The difference is that we statically put all bars at the bottom (window.innerHeight - 50) with a height of zero and then entered a transition with .transition(). From here on, we define the transition that we want.

First, we wanted each bar's transition delayed by 20 milliseconds using i*20. Most D3 callbacks will return the datum (or whatever datum has been bound to this element, which is typically set to d) and the index (or the ordinal number of the item currently being evaluated, which is typically i) while setting the this argument to the currently selected DOM element. If we were using, say, classes, this last point would be fairly important; otherwise, we'd be evaluating the rect SVGElement object instead of whatever context we actually want to use. However, because we're mainly going to use factory functions for everything, figuring out which context is assigned to this is far less of a worry.

This gives the histogram a neat effect, gradually appearing from left to right instead of jumping up at once. Next, we say that we want each animation to last just shy of a second, with .duration(800). At the end, we define the final values for the animated attributes--y and height are the same as in the previous code--and D3 will take care of the rest.

 

Save your file and refresh. If everything went according to plan, you should have a chart that looks like the following:

According to this, voter turnout was fairly high during the EU referendum, with the south-west having the highest turnout. Hey, look at this; we kind of just did some data journalism here! Remember that you can look at the entire code on GitHub athttp://github.com/aendrew/learning-d3-v4/tree/chapter1 if you didn't get something similar to the preceding screenshot.

We still need to do just a bit more, mainly using CSS to style the SVG elements. We could have just gone to our HTML file and added CSS, but then that means opening that yucky index.html file. Also, where's the fun in writing HTML when we're learning some newfangled JavaScript?

First, create an index.css file in your styles/ directory:

html, body { 
  padding: 0; 
  margin: 0; 
} 

.axis path, .axis line { 
  fill: none; 
  stroke: #eee; 
  shape-rendering: crispEdges; 
} 

.axis text { 
  font-size: 11px; 
} 

.bar { 
  fill: steelblue; 
} 

Then, just add the following line to the top of main.js:

import * as styles from 'styles/index.css'; 

I know. Crazy, right? No <style> tags needed!

Note

It's worth noting anything involving require() or import that isn't a JS file is the result of a Webpack loader. Although the author of this text is a fan of Webpack, all we're doing is importing the styles into main.js with Webpack instead of requiring them globally via a <style> tag. This is cool because, instead of uploading a dozen files when deploying your finished code, you effectively deploy one optimized bundle. You can also scope CSS rules to be particular to when they're being included and all sorts of other nifty stuff; for more information, refer to https://github.com/webpack-contrib/css-loader

 

Looking at the preceding CSS, you can now see why we added all those classes to our shapes. We can now directly reference them when styling with CSS. We made the axes thin, gave them a light gray color, and used a smaller font for the labels. The bars should be light blue. Save this and wait for the page to refresh. We've made our first D3 chart:

I recommend fiddling with the values passed to .width and .height to get a feel of the power of D3. You'll notice that everything scales and adjusts to any size without you having to change other code. Smashing!

Summary


In this chapter, you learned what D3 is and took a glance at the core philosophy behind how it works. You also set up your computer for prototyping of ideas and to play with visualizations. This environment will be assumed throughout the book.

We went through a simple example and created an animated histogram using some of the basics of D3. You learned about scales and axes, that the vertical axis is inverted, that any property defined as a function is recalculated for every data point, that we use a combination of CSS and SVG to make things beautiful. We also did a lot of fancy stuff with ES2017, Babel, and Webpack, and got Node.js installed. Go us!

Most of all, this chapter has given you the basic tools so that you can start playing with D3.js on your own. Tinkering is your friend! Don't be afraid to break stuff--you can always reset to a chapter's default state by running $ git reset --soft origin/chapter1, replacing 1 with whichever chapter you're on.

Next, we'll be looking at all this in a bit more in depth, specifically how the DOM, SVG, and CSS interact with each other. This chapter discussed quite a lot, so if some parts got away from you, don't worry. Just power through to the next chapter, and everything will start to make a lot more sense.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Build interactive and rich graphics and visualization using JavaScript`s powerful library D3.js
  • Learn D3 from the ground up, using the all-new version 4 of the library
  • Gain insight into producing high-quality, extensible charts and visualizations using best practices such as writing testable, extensible code and strong typing

Description

Want to get started with impressive interactive visualizations and implement them in your daily tasks? This book offers the perfect solution-D3.js. It has emerged as the most popular tool for data visualization. This book will teach you how to implement the features of the latest version of D3 while writing JavaScript using the newest tools and technique You will start by setting up the D3 environment and making your first basic bar chart. You will then build stunning SVG and Canvas-based data visualizations while writing testable, extensible code,as accurate and informative as it is visually stimulating. Step-by-step examples walk you through creating, integrating, and debugging different types of visualization and will have you building basic visualizations (such as bar, line, and scatter graphs) in no time. By the end of this book, you will have mastered the techniques necessary to successfully visualize data and will be ready to use D3 to transform any data into an engaging and sophisticated visualization.

What you will learn

Map data to visual elements using D3 s scales Draw SVG elements using D3 s shape generators Transform data using D3 s collection methods Use D3 s various layout patterns to quickly generate various common types of chart Write modern JavaScript using ES2017 and Babel Explore the basics of unit testing D3 visualizations using Mocha and Chai Write and deploy a simple Node.js web service to render charts via HTML Canvas Understand what makes a good data visualization and how to use the tools at your disposal to create accurate charts

What do you get with a Packt Subscription?

Free for first 7 days. $15.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details


Publication date : Apr 28, 2017
Length 308 pages
Edition : 3rd Edition
Language : English
ISBN-13 : 9781787120358
Category :

Table of Contents

18 Chapters
Title Page Chevron down icon Chevron up icon
Credits Chevron down icon Chevron up icon
About the Authors Chevron down icon Chevron up icon
About the Author2 Chevron down icon Chevron up icon
About the Reviewer Chevron down icon Chevron up icon
www.PacktPub.com Chevron down icon Chevron up icon
Customer Feedback Chevron down icon Chevron up icon
Preface Chevron down icon Chevron up icon
Getting Started with D3, ES2017, and Node.js Chevron down icon Chevron up icon
A Primer on DOM, SVG, and CSS Chevron down icon Chevron up icon
Shape Primitives of D3 Chevron down icon Chevron up icon
Making Data Useful Chevron down icon Chevron up icon
Defining the User Experience - Animation and Interaction Chevron down icon Chevron up icon
Hierarchical Layouts of D3 Chevron down icon Chevron up icon
The Other Layouts Chevron down icon Chevron up icon
D3 on the Server with Canvas, Koa 2, and Node.js Chevron down icon Chevron up icon
Having Confidence in Your Visualizations Chevron down icon Chevron up icon
Designing Good Data Visualizations Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%

Filter reviews by


No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.