Now that we’re familiar with the basic structure of an SVG image and its elements, how can we start generating shapes from our data?
You may have noticed that all properties of SVG elements are specified as attributes. That is, they are included as property/value pairs within each element tag, like this:
Hmm, that looks strangely like HTML!
We have already used D3’s handy
attr() methods to create new HTML elements and set their attributes. Since SVG elements exist in the DOM, just as HTML elements do, we can use
attr() in exactly the same way to generate SVG images!
First, we need to create the SVG element in which to place all our shapes.
That will find the
body and append a new
svg element just before the closing
</body> tag. While that will work, I recommend:
Remember how most D3 methods return a reference to the DOM element on which they act? By creating a new variable
svg, we are able to capture the reference handed back by
append(). Think of
svg not as a “variable” but as a “reference pointing to the SVG object that we just created.” This reference will save us a lot of code later. Instead of having to search for that SVG each time — as in
d3.select("svg") — we just say
Alternatively, that could all be written as one line of code:
See the demo for that code. Inspect the DOM and notice that there is, indeed, an empty SVG element.
To simplify your life, I recommend putting the width and height values into variables at the top of your code, like this (view the source):
I’ll be doing that with all future examples. By variabalizing the size values, they can be easily referenced throughout your code, as in:
Also, if you send me a petition to make “variabalize” a real word, I will gladly sign it.
Time to add some shapes. I’ll bring back our trusty old data set
and then use
data() to iterate through each data point, creating a
circle for each one:
selectAll() will return empty references to all
circles (which don’t exist yet),
data() binds our data to the elements we’re about to create, and
join() finally adds a
circle to the DOM.
To make it easy to reference all of the
circles later, we can create a new variable to store references to them all:
Great, but all these circles still need positions and sizes. Be warned: The following code may blow your mind.
Feast your eyes on the demo. Let’s step through the code.
Takes the reference to all
circles and sets the
cx attribute for each one. Our data has already been bound to the
circle elements, so for each
circle, the value
d matches the corresponding value in our original data set (5, 10, 15, 20, or 25). Another value,
i, is also automatically populated for us.
i is a numeric index value of the current element. Counting starts at zero, so for our “first” circle
i == 0, the second circle’s
i == 1 and so on. We’re using
i to push each subsequent circle over to the right, because each subsequent loop through, the value of
To make sure
i is available to your custom function, you must include it as an argument in the function definition (
function(d, i)). You must also include
d, even if you don’t use
d within your function (as in the case above).
On to the next line.
h is the height of the entire SVG, so
h/2 is one-half of its height. This has the effect of aligning all
circles in the vertical center.
Finally, the radius
r of each
circle is simply set to
d, the corresponding data value.
Color fills and strokes are just other attributes which you can set using the same methods. Simply by appending this code
we get the following (see demo):
Of course, you can mix and match attributes and custom functions to apply any combination of properties. The trick with data visualization, of course, is choosing appropriate mappings, so the visual expression of your data is understandable and useful for the viewer.