In part 2 of this series on using the D3 Javascript library (d3.js), we will look at one way to setup data to be consumed by D3 via a tab-separated values data string copied directly out of Google Sheets. We will also be introduced to the dataset that we’ll be using throughout, a list of lay off casualties derived from the publicly available team information on the website of my former employer, Canada Learning Code.

Note: if you’re just coming to this series or need a refresher on the story so far, why not check out the first post?

What is D3 (and D3.js)?

D3 (https://d3js.org), short for Data-Driven Documents, is a JavaScript library that is used for data visualisation. The “Documents” of D3 refer to the document object model (DOM) of a webpage, with which this library interacts. From D3’s own website page (“What is D3?”, https://d3js.org/what-is-d3:

D3 is not a charting library in the traditional sense. It has no concept of “charts”. When you visualize data with D3, you compose a variety of primitives.D3 makes things possible, not necessarily easy; even simple things that should be easy are often not. To paraphrase Amanda Cox: “Use D3 if you think it’s perfectly normal to write a hundred lines of code for a bar chart.”

Sounds perfect.

What data will we (can we) use?

The main constraint for data is that it has to be publicly available. Being a registered Canadian not-for-profit and charity, there is actually a lot of information that can be accessed by way of the organisation’s annual statutory filing, the T3010 Registered Charity Information Return. I don’t need to go that far to find what I want: the information that I want can be found on Canada Learning Code’s own website at the bottom of the Our Team page (https://www.canadalearningcode.ca/our-team/).

The plan

The plan is simple and easily reproducible:

  1. Grab a historic version of the Our Team page from the Internet Archive’s WayBack Machine (https://web.archive.org/) from some time around Judgment Day but before the webpage was updated.
  2. Grab an up-to-date or updated version of the Our Team page.
  3. Tabulate the roles of the people listed for the HQ Team.
  4. Cross-reference the people listed for the HQ Team between page versions: If they aren’t present in the updated version, they’re likely (enough) to have been laid off. Flag these people.

There are few enough folks that I will do this manually, though it would probably be quite simple to scrape this information and set up a few rules to automatically cross-reference it as well.

Additional data notes

  • Since there exists no publicly-accessible organisational chart (AFAIK), I won’t be -grouping people into their organisational units.
  • People can still be functionally grouped through reasonable inference based on their role title and a rough hierarchy will be coded, from 0 to 4, where:
    • 1 = “manager”
    • 2 = “director”
    • 3 = “chief” (non-executive)
    • 4 = “chief executive”
    • 0 = everyone else (“non-manager”)
  • The hierarchy coding doesn’t distinguish between the different types of managers, (e.g., people managers versus product or program managers), nor does it include within-level seniority (e.g., managers versus senior managers).
  • I won’t be using names, though they are publicly listed alongside the rest of the information being used.
  • I won’t be differentiating between full-time and part-time people because that information doesn’t seem outwardly apparent or available.

Getting D3 Set Up

There are lots of ways to load D3, however I will be using an import statement to grab it as an ECMAScript module (ESM) bundle from the JavaScript CDN jsdelivr.net1:

<!DOCTYPE html>
<script type="module">
    import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
    // your code goes here
</script>
<div id="d3-container">
    <!-- this will hold whatever D3 generates -->
</div>
  1. Note how the <script> tag has a type attribute of module2: this is necessary to be able to make use of the import statement. Without it, you’ll get an error message along the lines of: SyntaxError: Cannot use import statement outside a module. After the import statement, we can start using the different D3 components on our page throught the d3 object/namespace.
  2. I’ve also created a <div> with id="d3-container", which will be the target for the elements created with D3.

Loading the data

Did you know that if you drag-select and copy cells from Google Sheets, they will be pasted as a tab-separated values (tsv) string? I’ve gathered the data that I’ll be working with as detailed above and I’ve staged it in a Google Sheets document structured as follows:

Role LaidOff Functional Hierarchy
string, role title/name as written on website bool-ish 3, "TRUE" if the role disappeared between updates otherwise "FALSE" string, The role’s functional unit that’s been inferred from the role title, e.g. MARCOM, HR, FUND, etc. int, value from 0 to 4 codifying the role’s relative position in the organisation’s control structure.

Copying and pasting the data into an IDE yields the following string, which I’m assigning to a variable, rawData.

Show data string
const rawData = `Role	LaidOff	Functional	Hierarchy
Chief Executive Officer	FALSE		4
Chief Strategy & People Officer	FALSE		3
Director, Fund Development	FALSE	FUND	2
Director, Partnerships & Program Facilitation	FALSE		2
Director of Marketing and Communications	FALSE	MARCOM	2
Director of Finances & Accounting	TRUE	FIN	2
Director, Programs	FALSE		2
K-12 Program Manager	TRUE		1
Adult Program Manager	FALSE		1
Manager, Chapters	FALSE		1
Senior Manager, Partnerships	FALSE		1
Senior Manager, Program Facilitation	FALSE		1
Senior Project Manager	TRUE		1
Sr Manager Evaluation & Impact Measurement	TRUE	EVAL	1
Senior People and Culture Manager	FALSE	HR	1
Senior Marketing Manager	FALSE	MARCOM	1
Instructional Training Manager	FALSE		1
Senior Fund Development Specialist	TRUE		0
Sr. Learning Experience Designer	TRUE		0
Senior Partner Development Lead	TRUE		0
Senior Learning Facilitator	TRUE		0
Senior Learning Facilitator	TRUE		0
Senior Learning Facilitator	TRUE		0
Senior Fund Development Lead	FALSE	FUND	0
Senior Fund Development Lead	FALSE	FUND	0
Partnership Development	TRUE		0
Lead, Teen Ambassador Program (TAP)	TRUE		0
Senior Partnership Development Lead	TRUE		0
Senior Bilingual Learning Facilitator	TRUE		0
Senior Bilingual Learning Facilitator	TRUE		0
Bilingual learning Facilitator	TRUE		0
Learning Experience Designer, Adult Programs	TRUE		0
Learning Facilitator	TRUE		0
Learning Facilitator	TRUE		0
Learning Facilitator	TRUE		0
Learning Facilitator	TRUE		0
Bilingual, Partnership Development	TRUE		0
Accountant	FALSE	FIN	0
People and Culture Coordinator	TRUE	HR	0
Data Analyst	TRUE	EVAL	0
Partnerships Coordinator	TRUE		0
Marketing Coordinator	FALSE	MARCOM	0`

With a well formed tsv string, I can use the d3.tsvParse() method to–you guessed it–parse the tsv string into an array of objects, using each column name as a property.

const data = d3.tsvParse(rawData);
//console.log(data); 
// [ 
//     { "Role": "Chief Executive Officer", "LaidOff": "FALSE", "Functional": "", "Hierarchy": "4" },
//     { "Role": "Chief Strategy & People Officer", "LaidOff": "FALSE", "Functional": "", "Hierarchy": "3" },
//     // and 40 other entries ...    
// ]

Our first (pie) chart

See the code:
<script type="module" defer>
  import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
  
  const data = [1, 2, 3, 4, 5];

  const height = 300;
  const width = 300;
  const margin = 30;

  const outerRadius = Math.min(width,height) / 2 - margin;

  const arcGenerator = d3.arc()
    .innerRadius(outerRadius * .57)
    .outerRadius(outerRadius)
  ;
  
  const pieGenerator = d3.pie()
    // .value(d => d)
  ;
  
  const svg = d3.create("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("viewBox", [-width /2, -height /2, width, height])
    .style("margin-inline", "auto")
    .style("width", "100%")
  ;
  
  svg.append("g")
    .selectAll()
    .data(pieGenerator(data))
    .join("path")
      .attr("fill", d => d3.interpolateGnBu(d.value/d3.max(data)))
      .attr("stroke", "none")
      .attr("d", arcGenerator) // SVG attribute for drawn path
  ;
  
d3.select("#d3-container").node()
  .append(svg.node());
 
</script>
<div id="d3-container"></div>

From here, we can begin to use (some of) the rest of D3’s functionality to explore the data. The “100 lines of code” is, in this case, a bit of an exaggeration, but it’s true that there’s more than a little bit of setup and preparation necessary to make even a simple chart. Let’s take a pie chart4, for example:

  1. A pie chart is made of slices. In D3, a pie slice (or circle fraction) is called an arc.
  2. An arc is defined by an innerRadius, an outerRadius, a startAngle, and a stopAngle.
  3. It would be a hassle to have to determine these ourselves, but D3 has a pie generator, d3.pie() which can calculate the appropriate values for startAngle and stopAngle based on data that we provide.

Even with those tools to help us, we’re still responsible for creating the scalable vector graphics (SVG) elements and inserting them into the DOM. SVGs are images that are made of paths defined in code. Behind the scenes, each pie segment in the example above looks a little something like:

<path 
  fill="rgb(211, 238, 206)" 
  stroke="none" 
  d="M-48.808,-109.625A120,120,0,0,1,0,-120L0,-68.4A68.4,68.4,0,0,0,-27.821,-62.487Z">
</path>

The fill and stroke attributes are probably self-explanatory, but the d attribute is where the magic happens: this defines what shape the path will take when drawn–in our case, how the pie segment will appear. Thankfully, we’re not the ones coming up with these numbers: we can provide data to d3.arc() and have it compute the appropriate paths.

Planning the next move

  1. import D3 ECMAScript module inside a <script> element with type="module"
  2. Create a container <div> for our future chart.
  3. Format data as tsv string and load with d3.tsvParse().
  4. Create a root <svg> element to contain the chart graphics, defining a width and height.
  5. Create a <g> (group) element to contain the <path> elements that will make up the pie chart.
  6. Use d3.pie() and d3.arc() to generate the code for the pie chart slices, based on some data that we’ll provide.
  7. Put the generated pie chart into the container <div>.
  8. Admire our handiwork! 💪

Are you having fun yet? We’ll pick this up in the next post, Data-Driven Disappointment Part 3: Dealing with data


  1. D3 also plays nice with other popular frameworks such as React and Svelte. Check out their Getting Started page for more details on the different ways you can use D3. 

  2. Learn more about the differences between JavaScript modules and standard scripts here: MDN: JavaScript Modules # Other differences between modules and standard scripts 

  3. bool-ish because it’s a string that has the vale of "TRUE" or "FALSE" but not a true bool as of writing. 

  4. A pie chart is a way of representing fractions or proportions, with each group carving out a certain amount of “chart angle” (pie slice) based on its size relative to other groups.