D3 101. Let us get started with the basics!

I’m told you want to build powerful and beautiful visualizations using D3; however, you can’t run before you walk! Don’t worry, though, you are in the right place to learn the basics of this powerful library. So, let us get started!

The code blocks examples I’ve prepared, are interactive; so, you can change the code and see what happens! Remember to press the button “run” to visualize the result of running the code. If you want to reverse the code blocks to its original state, just press the button “reset“.

1. What is D3?

First things first. D3 is an open source JavaScript library used to manipulate visual elements based on data.

D3 is rather low-level; this means that instead of offering a ready-to-use-solution, D3 provides you with the building blocks to construct any (and I mean any) kind of visualization. This makes D3 extremely versatile!

Of course, there are times when you don’t want to build a solution from the ground up, especially when we are talking about commonly used charts. Fear not, nonetheless! D3 enjoys an active and generous community; thus, you will be able to find a wide variety of superb visualizations ready for your use.

2. Drawing on the browser

Now, how can we draw graphics on the browser? Basically, there are two paths: using <svg> or using <canvas> elements. Each path has its own pros and cons, which you can explore in this excellent article.

While D3 can be exploited in combination with the <canvas> element, more likely than not we are going to see it being used with SVG elements; why is that? Well, this technique offers the facility to link each piece of information (this is, the data we want to visualize), with a single SVG element. In turn, this makes it easy to manipulate each graphic element, making them visually reflect any change in the underlying data they are linked to.

2.1. The SVG elements

Then, what are the SVG elements? Simply put, they are a special type of HTML element. They are used to draw shapes on the browser but instead of relying on a pixels grid, as a JPG or PNG image, the SVG elements provide the instructions required to draw the figure they represent.

By the way, SVG stands for “Scalable Vector Graphics”; and, as its name implies, one of the greatest advantages of these kinds of images is they can adjust to any size without suffering blurring when enlarging the image, as would be the case with conventional image formats. This is possible given that the SVG elements contain the blueprints to draw the image; hence, these instructions can adapt to any size.

One thing to keep in mind is: in order to visualize the SVG elements, it is necessary to embed them inside a <svg> element; otherwise, they will not appear on the browser

If you’d like to know more about SVG, this article is a great resource.

Now, let us explore some properties of the SVG components we will use while building our visualizations in D3.

2.1.1. The <svg> element

  • They can be thought of as the “canvas” where all the graphic elements will be drawn.
  • All the other SVG elements must be embedded inside this element.
  • Its most important attributes are width and height, that, as their name indicates, are used to set the width and height of the <svg> element.

2.1.2. General points.

  • There are some common atributes shared by all the SVG elements; like stroke, used to set the color of the border of your shapes; and stroke-width useful to adjust the thickness of the border.
  • The position of any figure is determined taking as reference the upper left corner of the parent <svg> element.

2.1.3. The <rect> element

  • We can use this element to create rectangles or squares.
  • Its x and y attributes are used to set the vertical and horizontal position of the shape, respectively. The position is determined taking as reference the upper left corner of the rectangle
  • With the width and height attributes, we can adjust the width and height of the figure.
  • We use the fill attribute to change the inner color of the figure.

Example:

2.1.4. The <circle> element

  • Its cx and cy attributes are used to set the vertical and horizontal position of the shape, respectively. In this case, the center of the circle is taken as reference to measure the position.
  • With the r attribute, we can adjust the radius of the circle.

Example:

2.1.5. The <ellipse> element

  • This shape is very much alike to a circle, with the particularity that in this case we need both: a vertical and a horizontal radius.
  • Its cx and cy attributes are used to set the vertical and horizontal position of the shape, respectively. The center of the ellipse is taken as reference to measure the position.
  • With the rx and ry attributes, we can adjust the vertical and horizontal radius of the ellipse.

Example:

2.1.6. The <line> element

  • Its x1 and y1 attributes are used to set the position of the start of the line.
  • With the x2 and y2 attributes, we can adjust the position of the end of the line.

Example:

2.1.7. The <text> element

  • It is used to create labels with text.
  • Its x and y attributes are used to set the vertical and horizontal position of the shape, respectively.
  • The text-anchor attribute is used to determine what point of the label will be used as reference to determine its position respect to its parent. By default, its value is “start”, which means that the point of reference is the lower left corner. Other values are “end”, to use the right lower corner; and “center”, to take the lower center position of the label as reference.
  • With the fill attribute, we can set the color of the text.
  • Using the font-size attribute, we can alter the size of the font.

Example:

2.1.8. The <path> element

  • It is the most versatile of the SVG elements. With it, we can draw any shape we can think of; nevertheless, it is highly complicated to draw with this element.
  • Using the d attribute, we can indicate the form of the shape we want to display.
  • Learning how to draw shapes with this element would require an article (or a series of articles) on its own. Do not worry though. D3 is capable of generating the text strings required for the d attribute to draw a wide and versatile array of shapes.

Example:

2.1.9. The <g> element

This element is a peculiar pal. On its own, it doesn’t draw any shape on the browser; rather, it is used to group related SVG elements. Its children inherit all its inheritable attributes. This is especially useful in the case we apply transformations; since the effect of this operation is passed to all the child SVG elements.
Example:

2.1.10. The <title> element

Actually, the <title> element is not an SVG element; however, embedding it inside one of them has the interesting effect of creating a “tooltip text” that appears every time you hover above the respective SVG element.

Go ahead! position the mouse’s pointer over the ellipse in the following example and see what happens.
Example:

3. Manipulating the DOM with D3

Before anything else, remember that D3 is a JavaScript library; hence, you must load it on your browser’s window. As of the moment of writing this post, the latest version of D3 is 5; then, you must include this snippet in your page before using it:

<script src="https://d3js.org/d3.v5.min.js"></script>

After the browser loads your page, you’ll have available the object d3.

3.1. The selection object

It is time to start manipulating the DOM; the object in your browser that keeps track of all you see in the window (and many things you don’t see!)

The first thing we need to do is select some element; for this, we can use either: d3.select(selector) or d3.selectAll(selector). They both return a selection object; the difference is that the former selects a single HTML element and the latter encompasses a group of elements.

The selector argument is, more often than not, a text string with the format of a CSS selector. This argument can also be a reference to a DOM node, this is, a registered HTML element. You can access these nodes with, for example, the method document.getElementById(id). This can prove useful if you alrready have such reference and need to manipulate the same node throughout your code.

One interesting feature of the selection object is that, in turn, you can call either, select(selector) or d3.selectAll(selector) methods upon it. This can come in handy at times when we wan to perform a subselection of elements inside the selection object. For instance, If we wanted to select all circles inside an <svg> element with id “svg-container”, we could use the next snippet:

d3.select("#svg-container").selectAll("circle");

The selection object possesses several methods and, as a general rule, these methods return the selection object that called them. This is very useful to apply method chaining, a pattern that D3 favors immensely so that we arrange our code more concisely.

What do you think if we explore some of the basic methods of the selection object?

3.2. The selection.attr(name, value) method

  • With this method we can adjust the attributes of the HTML elements, as color, position, stroke, etc.
  • The name parameter is a text string with the name of the parameter we are interested on.
  • The value parameter is optional; if present, it can directly take the value we want to assign to the parameter name. In this case, the method attr returns the selection object that called it.
  • The value parameter can also take the form of a function. This will be explored in an upcoming section.
  • If the value parameter is omitted, the method returns the value currently assigned to the parameter name.

Example:

3.3. The selection.html(value) method

  • With this method you can write all the markup content inside the DOM node referenced by the selection object.
  • In its simplest form, the value parameter is a text string with HTML format.
  • The value parameter could also be a function that returns a string with HTML format. More on this function will be discussed in an forthcoming section.

Example:

3.4. The selection.append(type) method

  • With this method we can add new elements inside a container.
  • In this case, the selection object makes reference to the container where the new element will be added.
  • The type parameter is usually a text string with the tag name of the element we want to append in the container; nevertheless, it can also be a function that returns a reference to a DOM node.
  • This method returns a selection object that, unlike most methods of the selection object, makes reference not to the selection object that called it, but to a selection of the newly appended HTML element.

Example:

3.5. Using a function to fill the value of an argument

As you could notice, many methods of the selection object accept as an argument either: a constant value or a function, we will be calling this latter the function-argument. Example of this sort of methods are: selection.append(type), selection.html(value), or selection.attr(name, value).

The value returned by the function-argument will be used as the value for the parameter in the particular selection object method.

It is interesting to notice that, in turn, the function-argument receives automatically 3 arguments:d, i, and nodes.

The d parameter is the current datum. And, what on earth does that mean? The power of D3 lies in its capability to link pieces of information with graphical elements. The current datum refers precisely to the particular piece of information linked to each of the DOM nodes. I don’t want to dive too deep in the mechanism to perform this joining, but you can explore it in my post for the Update Pattern

The i parameter is the current index, this is, the index given to each node inside the selection object. This index depends on the order in which the nodes were disposed inside their container when they were selected.

The nodes parameter referes to the current group, i.e., a NodeList object with a reference to every node in the selection object.
Example:

3.6. The d3.create(name) method

Just as the case of the selection.append(type), this method creates a new DOM node and returns a selection object with a reference to it; nevertheless, unlike the former, the node created by this method is detached; therefore, we need to add it to some container present in the browser if we want to visualize this newly created node.

The name parameter is a text string with the tag name of the HTML element you want to create.

Now, I’d like to share with you a very important detail if you want to create SVG elements. Not knowing this caused me a lot of pain and took me several hours to discover what was going on. The secret is this: YOU MUST CREATE SVG ELEMENTS IN THE SVG NAMESPACE; otherwise, you won’t visualize them even if they are registered in the DOM. How do you do this? It’s quite simple, you only need to prepend “svg:” to the tag name of the element you want to create; for example, to create a circle:

        d3.create("svg:circle");

Let’s see a dynamic example. In the next piece of code you’ll notice the use of the method selection.node() when we are appending the detached element inside the <svg> canvas. What this method does is return the DOM node linked to the selection object.
In the following example, I want you to notice how I’m using a function to feed the method .append(). As we saw, this function returns a node element of the DOM.

4. Wrapping up

D3 is a very extensive library. In this article, I’ve just outlined the main methods and concepts that are going to allow you to start tweaking and playing with the library.

In contrast to other visualization tools, D3 is relatively low-level; this means that rather than a ready-solution general-purpose software, D3 offers you basic building blocks for you to create any kind of visualization your mind may come up with.

Where to go from here? I think the final ingredient to start building awesome graphs is to learn about the Update Pattern, that is going to enable you to create links between visual elements and data.