Ensuring your charts are responsive is essential if you want them to work regardless of the user's device and screen size. In this lesson we'll learn how the viewBox attribute of SVG elements works, and apply that knowledge to make a future-proof D3 chart.
Making your D3 charts responsive can be accomplished in a number of ways, but we're going to take a look at using the viewBox attribute of SVG tags. We've got a simple example here, where we've got a div which has got a couple of funds inside that div.
If we look at our page here, we can see that our div resizes as we resize the page, but our SVG tag is static. It's not going to change based on the browser resizing. Now, our buttons here just call our resize SVG function, pass in dimensions and then that function just updates the width and height attributes of our SVG tag.
If we actually go try those out, we can see that our SVG blue background does change sizes, but our chart itself doesn't resize, and that's what we're going to fix. If we look at the code that's actually building this chart, we can see that we're just selecting that chart div, and then appending our SVG tag, setting it's width and height, and then we're calling this function that I've named init.
Before we get to that, we've got our within their height defined up here. We've got a very simple array of data, and then we've got some X and Y scales that we're going to use just to make sure that our chart fills the SVG tag, so we'll use those when sizing our bars.
If we now look at the init function, you can see we just use the regular D3 code to create a rect shape for each piece of our data. We set it's fill style to be green, and then we're just using the scales to set our X and Y, and width and height for all of those bars.
We're using those to ensure that everything fills that area. You can see that it is, in fact, working. Now, the viewBox attribute that we're going to be taking a look at is explained in a lot of detail, and with a lot of other stuff in this absolutely excellent series of articles by Sarah Suidon that I highly, highly recommend you checking out.
We're interested in the viewPort and the viewBox, so if we're going to scroll down here and just show you a couple important pieces here. This tells us that the viewPort is the viewing area where the SVG will be visible. You can think of the viewPort as a window through which you can see a particular scene.
For our purposes, at least, you can essentially think of the viewPort as the SVG tag itself. Whatever width and height attributes we put on that SVG tag, that's going to be the size of our viewPort. Now, if we come down and look at this section of the article that discusses the viewBox that we're going to be using, it says you can think of the viewBox as the real coordinate system, because it's the coordinate system used to draw the SVG graphics.
The article also tells us that you specify your own user coordinate system using the viewBox attribute. If the system has the same aspect ratio as the viewPort coordinate system, it'll stretch to fill it. Essentially, otherwise, is not going to fill it because it's going to be off in one direction or another.
In those cases, use an attribute called preserveAspectRatio, which we're not really going to get into in this lesson. The important thing is that this is going to stretch to fill the viewPort area. The dimensions that we give in the viewBox attribute are going to stretch to make the attributes on the SVG tag, the width and height attributes on the SVG tag.
Knowing that, we can take a look at what's actually going on here, in our example. Before we go on, I'm actually going to change the size here to 600 by 600, just so that we can fit everything in our viewPort here, and not have to scroll around. Then we'll go ahead and let's make this padding a little smaller, so now everything actually fits in there and we can see exactly what's going on.
Now, if we take our existing code and just add the viewBox attribute to the SVG tag, we'll see how that changes the behavior here. The viewBox attribute takes a space separated list, not comma separated, so we're just going to do zero zero to start at the top left, and then give it the same width and height.
You can see now, when we resize our SVG tag, our actual chart scales to fill that tag. You can see, in the inspector here, our viewBox is still 400 by 400, but the SVG has changed to 600 by 600 and it's then scaling up to fill that space, just like we talked about before.
They really are being scaled. They're not being redrawn to fill the space, it's simply scaling those vectors. Just by adding that attribute, you do get some benefits of making sure that your chart actually fills the space. Now, one thing I want to show here is that the way that viewBox behaves can be a little bit counter-intuitive at times.
If we divide our width and height in half here, so our width and height is 400, we're going to set our viewBox to be 200 by 200. What's going to happen if we refresh? Well, it turns out that the chart is too big to actually fit in the viewPort, it's too big to fit in the SVG tag.
What's going on here is it's actually drawing the chart in a 200 by 200 square, essentially the top left corner of our SVG tag, and then that viewBox is actually getting scaled up to fill the 400 by 400 space, and that means that our bars in our chart are actually mostly out of view here.
It's being drawn small, and scaled up entirely, and now it doesn't fit in our area. You can see, if we change the size of the SVG tag, the relationship stays the same. What we actually can do is, if we want it smaller, we can set our viewBox to be twice as big as our SVG tag, and then when the viewBox gets scaled down to fit in that width and height, our chart actually gets scaled down.
Now you can see that our chart is actually filling up half as much room as the SVG tag has. It's sort of backwards there, but if you think about how the scaling and things work, it does make sense. I definitely encourage you to check out that article more, because it really explains it in a lot of depth, and even has an interactive explorer where you can play with different settings.
Now that we have a basic understanding of the underlying pieces, I want to take a look at another article that provided the base implementation of what I generally use these days. It's an article by Brendan Sudol, and it's exactly what we're looking to learn about here, responsive D3.
He has abstracted this stuff into a nice little function called Responsify, where essentially you just pass in any SVG tag, and it makes your chart responsive. We're going to go ahead and paste in his code, into our application here, and then we'll take a look at what this actually does.
In this function, we're going to pass in the SVG tag itself, and then this code is going to go and find the parent container. In our case, that's going to be our chart div, this tag here. Once it has a reference to that parent container, it's just going to get the width and height.
This code is actually going to get the width and the height, regardless of how those width and height are set, it'll get the actual measured dimensions of that. Lastly, it'll calculate the aspect ratio by dividing the width and the height. The next step in this function is to actually create that viewBox attribute that we had been manually setting before.
This is going to set the viewBox equal to the width and the height that is actually measures from the parent container. Then it's going to set that preserveAspectRatio attribute which we ignore for now, and then it's going to call a resize function.
This Responsify function also sets up a resize listener, so this is setting it up so that whenever the browser window gets resized, it's also going to call this resize function. It calls it when you initially call this function, or pass your SVG tag into this function, and then it'll also call this function any time the browser window resizes.
What does this resize function do? It gets the width of the parent container again. This is, again, going to be our enclosing div. It finds the current width of it, so maybe the page was resized, and it recalculates that width. Then it simply updates the SVG tags width and height attributes accordingly.
It sets the width to be the same as the container width, and then it sets the height based on that aspect ratio. In our case, our example here is just a one by one square, so that ratio doesn't come into play, but it is handy to have that there if you have a non-square chart, which is generally going to be the case.
This function is only going to be updating the width and height attributes of the SVG tag, but the fact that we have the viewBox set will ensure that everything stays scaled properly. We can come down here, now, to where we're creating ours, and get rid of the viewBox that we manually created.
Then we'll just add this call to Responsify when our chart initially gets created. We set up our chart, we build the initial size and everything, and then we pass that SVG tag through to Responsify, and that'll set up all of our responsiveness.
If we save that, you can look over here and see it's already the scaling to fill the space, and now if we look at the SVG tag, notice our viewBox is 400, but our width and our height are filling the available space. The SVG tag has been updated by the Responsify function to match the size of the chart, and everything is getting filled thanks to that viewBox attribute.
We can see these numbers update. If we resize our SVG tags, dimensions are getting updated, and our viewBox is staying the same, but everything is still filling that available area.