You want to work with D3, but you don’t understand how SVGs are rendered in the browser. This makes it very difficult to understand HTML code that D3 outputs. Let’s tackle some of these issues.
The code we will be looking at today was generated by D3. However, in this example, we’re not looking at D3. We’re looking only at the SVG code rendered in HTML.
This is very important because when working with D3, you will typically be creating SVGs. If you’re not familiar with how an SVG looks and behaves, debugging D3 code will be very difficult.
Before getting started, I want you to keep one thing in mind when looking at this code. SVG code, in its simplest form, is putting shapes on a canvas. In the example below, we will draw an SVG canvas and place shapes and text on that canvas. That’s all we’re doing. Don’t let this code deter you. It’s nothing more than drawings a canvas, and then drawing shapes on that canvas. There might be syntax you’ve never seen before, but learning is fun!
See the Pen Displaying an SVG graph in HTML by Bob Cavezza (@cavezza) on CodePen.
You view the full CodePen in your browser by clicking here.
Don’t forget that CodePen is interactive! You should interact with the CodePen and play around with the values. Change some values, see what happens. Remove an element and see how it changes how the graph is displayed on your screen.
Let’s start looking at the code.
First, we have an <svg>
tag that is creating an area where we can draw the elements we see. In essence, this tag is drawing our canvas. This creates a canvas that is 600 pixels wide and 400 pixels high. Now that we have the canvas ready, it’s time to start putting shapes on that canvas.
Now, to place items on this canvas, we have to tell the canvas the x and y coordinates for each item. On an SVG canvas, the coordinates start at the top left. If we want 0,0
, that would be the very top left. If we want 600,400
, that would give us the very bottom right. The graphic below should help explain this better than text can.
Let’s continue looking at the code. The next line of code is:
<g class="axis axis-y" transform="translate(40, 40)" fill="none" font-size="10" font-family="sans-serif" text-anchor="end">
<g>
is an interesting sub element of <svg>
. It’s the only element besides <svg>
itself that isn’t drawing a shape. <g>
a container that is used to group other SVG elements. “The important aspect of a <g>
element to know is that transformations applied to the <g>
element are performed on its child elements and its attributes are inherited by its children” (from Mozilla mdn). You can test this out in the CodePen by taking the transform of a grouping element, removing it from the g element, and putting it on each of its descendants.
What’s important to note is what the transform
attribute does. Transform defines a list of definitions that are applied to it and its children. The most common one you often see is translate(x, y)
. What this does is moves the item and its children to the right x amount of pixels and down y amount of pixels.
There are other, more complex transform options. You will be able to do some really cool animations when you learn transforms well and combine them with animations in D3.
In the next line, we encounter the <path>
element.
<path class="domain" stroke="#f00" d="M-6,0.5H0.5V320.5H-6"></path>
Path is an intriguing element and what I believe to be the most complex element in SVG land. In the <path>
element, the magic happens in the d=
area. I am still not able to decipher what the specific values inside the d
attribute do in a path element. When you draw more complex line graphs, that value can grow to hundreds or thousands of characters. What a path does is draws on the canvas. It is used to draw more complex objects than simple shapes.
The <path>
in our example is the only one that isn’t a simple line or a simple shape. This particular <path>
object draws a line with two ends sticking out of it. Instead of a typical line that looks like | (a line), they look like ] (a line with two lines sticking out at the ends). This might be the simplest <path>
element you’ll ever see as they get extremely more complex.
Here’s a screenshot of changing the color of that path so you can fully understand what I mean.
Although we’re not looking at it here, <path>
s are where D3 really starts to shine. Since the values in the d
attribute are so complex, a tool like D3 is important to be able to write code that other developers will understand when building these complex path objects.
On the next line, we run into the <g>
element again.<g class="tick" opacity="1">
Since we discussed the <g>
element earlier, we now know that this will add the tick
class to all of its descendants and will also pass the opacity
of 1 to all of its descendants (two elements – a <line>
element and a <text>
element).
Next, we see the <line>
element. As you might have guessed, a line element draws a simple line: <line stroke="#000" x2="520" transform="translate(0,29.59)"></line>
Stroke is the color of the line. x2
is the x coordinate where the line ends. x1
‘s default value is 0. Since there are no values for x1
, y1
, or y2
, we know that they all default to 0. Due to the inherited transforms, this specific element’s own “grid system” starts where the transforms have it start.
Next, we run into the <text>
element. And this SVG element does exactly what you expect it to, it renders text on the SVG canvas. The reason we have a <text>
element within SVG elements is so you can place it on the canvas by giving it x and y coordinates.
The x
attribute tells you where that element should be placed on the x
coordinate plane. The dy
attribute is a little different. This tells you where the element should be placed on the y axis in comparison to the previous element. In this example, we are placing it 0.32 ems away from the line element before it. If you’re not familiar with em
, it’s the same as the computed font-size of an element. Alternatives to use (instead of an em
) are pixel
s or rem
s. You can also use percentages in the dy
attribute.
What this first pattern does that we just looked at is draw the y axis and the horizontal lines along the y axis. Then, it draws the horizontal line on the grid next to 90 and placed the text of 90 onto the grid.
Now, you will see this pattern repeated a few times as the rest of those horizontal lines and text field numbers are placed on the grid.
Once that is complete, you will see an almost identical grouping of elements that add values to the x axis. Here is the complete code that draws all the lines of the canvas (not including the graph rectangles themselves). You should now be able to understand this entire block of code since we went through all of the building blocks above.
<g class="axis axis-y" transform="translate(40, 40)" fill="none" font-size="10" font-family="sans-serif" text-anchor="end">
<path class="domain" stroke="#f00" d="M-6,0.5H0.5V320.5H-6"></path>
<g class="tick" opacity="1">
<line stroke="#000" x2="520" transform="translate(0,29.59)"></line>
<text fill="#000" x="-10" dy="0.32em" transform="translate(0,29.59)">90</text>
</g>
<g class="tick" opacity="1" transform="translate(0,126.56)">
<line stroke="#000" x2="520"></line>
<text fill="#000" x="-10" dy="0.32em">60</text>
</g>
<g class="tick" opacity="1" transform="translate(0,223.53)">
<line stroke="#000" x2="520"></line>
<text fill="#000" x="-10" dy="0.32em">30</text>
</g>
<g class="tick" opacity="1" transform="translate(0,320.5)">
<line stroke="#000" x2="520"></line>
<text fill="#000" x="-10" dy="0.32em">0</text>
</g>
</g>
<g class="axis axis-x" transform="translate(40, 360)" fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
<path class="domain" stroke="#000" d="M0.5,6V0.5H520.5V6"></path>
<g class="tick" opacity="1" transform="translate(87.16,0)">
<line stroke="#000" y2="6"></line>
<text fill="#000" y="9" dy="0.71em">Mon</text>
</g>
<g class="tick" opacity="1" transform="translate(260.5,0)">
<line stroke="#000" y2="6"></line>
<text fill="#000" y="9" dy="0.71em">Tues</text>
</g>
<g class="tick" opacity="1" transform="translate(433.83,0)">
<line stroke="#000" y2="6"></line>
<text fill="#000" y="9" dy="0.71em">Wed</text>
</g>
</g>
What’s left is the drawing of the graphing elements themselves. At this point, I’m not sure if you would be surprised or expecting to learn that these three graphing elements are nothing more than rectangles.
Here is the full code to build the three rectangles on our graph:
<rect class="bar-line" transform="translate(40, 40)" height="320" width="153.33" x="10" y="0"></rect>
<rect class="bar-line" transform="translate(40, 40)" height="203.63" width="153.33" x="183.33" y="116.36"></rect>
<rect class="bar-line" transform="translate(40, 40)" height="132.52" width="153.33" x="356.66" y="187.47"></rect>
<rect>
s are one of the more simple SVG elements. You apply a height and width, and then also the starting x and y coordinates.
And that’s it for this visualization!
At this point, you know enough SVG code to be able to create graphs that you can reasonably expect to be needed at your job. Congratulations! Most people don’t fully understand SVGs in order to explain it to other engineers.
Today, you achieved that milestone!
At this point, you probably didn’t fully understand one or two of the elements from the CodePen. If that’s the case, you should go and change some of the CodePen values to see what happens to the visualization when those values are changed. Playing with code is the easiest way to make sure you understand it.