Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

How to Perfectly Fit an SVG’s ViewBox to its

Contents Using JavaScript


#javascript #html #webdev #tutorial

Nick Scialli (he/him) Jan 19 Originally published at typeofnan.dev ・8 min read

Using the SVG tag on websites is handy, but its interface can be different than we're used
to. In this post, we're going to see how to fit the viewport of an SVG to its contents every
time.

Please give this post a 💓, 🦄, and 🔖 if you want more


SVG/visualization posts!

The Problem
In some cases, we might have an SVG with some arbitrary shapes or paths in it. Those
shapes and paths may have specified dimentsions that don't always fit your viewport.
4 4 4
Let's consider the following SVG. We have two paths that I have taken from a map of the
United States. One path is for the state of Maryland and the other is for the state of New
York.

<svg style="height: 300px;">


<path
d="m 822.9,269.3 0,-1.7 h -.8 l 0,1.8 z m 11.8,-3.9 1.2,-2.2 .1,-2.5 -.6,-.6 -.7,.9 -.
></path>
<path
d="m 872.9,181.6 -1.3,.1 -.5,1 z m -30.6,22.7 .7,.6 1.3,-.3 1.1,.3 .9,-1.3 h 1.9 l 2.4
></path>
</svg>

What we might expect to see is this:

But if we were to fire up our web browser and navigate to this page, we wouldn't see a
thing. Why is that? Well, it's because these paths were taken from a full map of the U.S., so
the origin (0, 0) point of the coordinate system being used is at the top-left of the entire
country—next to Alaska.
This is no good as want our SVG to fit our two states perfectly.

Our Options
4 4 4
We have a couple reasonable options:
1. Transform each path such that it fits our current SVG view
2. Change our SVG "viewBox" to fit our paths
I personally like the second option and will go through that one in this post!

First, an Abstaction and a Tiny Bit of Math


Let's back off the states example for a moment and just use a couple arbitrary paths in an
SVG:

We can see that our SVG is 100x100, but our shapes actually only start at about x = 40 , y
= 30 and end somewhew around x = 80 , y = 90 . What we'd love is for our SVG to zoom
in on that area, so our view looks something like this:

4 4 4
How can we do this? It turns out the svg HTML element has a handy zoomBox attribute
that lets us pass the desired origin's x and y values along with the desired width and
height .

In the case of our shapes, we have the following:

4 4 4
We can see that our origin is at x=40, y=30 and then we have to do a little math for our
width and height:

width = xMax - xMin = 80 - 40 = 40


height = yMax - yMin = 90 - 30 = 60

Therefore, we might specify the following viewBox for our SVG.

<svg viewBox="40 30 40 60">


<!-- Shapes here -->
</svg>

And this works! Note that, by default, the SVG will center the objects in its viewBox rather
than distorting them.

That Seems Tedious


Yes, it would be super tedious to do this math any time we want to use SVGs, and seems
nearly impossible if we dynamic SVG children or many children.
Luckily, we're programmers! We enjoy automating things, so let's automate the process
we just did in our heads.

Automatically Finding the Boundary


The way I like to automatically find the boundary is to iterate through all children of the
svg . I use a native svg method called getBBox() that will return the x , y , width , and
height of an element. Then, just some simple comparison: if the x is lower than all the
other x values we have seen so far, it's the new xMin . Same with y . To get xMax and yMax ,
we do a similar operation, except we have to make sure we're looking at x + width and y
+ height for each element since x and y only give us the top-left point of the element
and we want the bottom-right.
Here's how this code might look:

const svg = document.querySelector('svg');

const { xMin, xMax, yMin, yMax } = [...svg.children].reduce((acc, el) => {


const { x, y, width, height } = el.getBBox();
if (!acc.xMin || x < acc.xMin) acc.xMin = x;
if (!acc.xMax || x + width > acc.xMax) acc.xMax = x + width;
if (!acc.yMin || y < acc.yMin) acc.yMin = y;
if (!acc.yMax
4 || y + height > acc.yMax)
4 acc.yMax = y + height;
4
return acc;
}, {});

We use document.querySelector('svg') to grab the only SVG on our page (but make sure
to use an ID or class if you need a different selector!). Next, I use [...svg.children] to (a)
get all of the child elements of our svg and (b) use the spread operator ( ... ) to convert
the HTMLCollection to an array of elements. Have an array enables me to use the reduce
method, which takes a callback function and an initial accumulator (an empty object {} ).
Within the reduce callback, I use the getBBox() method on each element to get the x, y,
width , and height . Using the aformentioned logic, we conditionally overwrite the xMin ,
xMax , yMin , and yMax , properties on our accumulator.

Our one final step is to set he viewBox attribute of the parent svg . Remember, the viewBox
is set to x y width height , so we have to convert our xMax and yMax to width and height ,
respectively!
We can do this with the math we discussed in our simplified example:

width = xMax - xMin


height = yMax - yMin

Let's put it all together here:

const svg = document.querySelector('svg');

const { xMin, xMax, yMin, yMax } = [...svg.children].reduce((acc, el) => {


const { x, y, width, height } = el.getBBox();
if (!acc.xMin || x < acc.xMin) acc.xMin = x;
if (!acc.xMax || x + width > acc.xMax) acc.xMax = x + width;
if (!acc.yMin || y < acc.yMin) acc.yMin = y;
if (!acc.yMax || y + height > acc.yMax) acc.yMax = y + height;
return acc;
}, {});

const viewbox = `${xMin} ${yMin} ${xMax - xMin} ${yMax - yMin}`;

svg.setAttribute('viewBox', viewbox);

And we can see it in action with our state SVGs. In fact, we touted the flexibility of this
solution, so it better be able to accommodate an additional state! Let's add North
Carolina for good measure.

4 4 4
CodeSandbox https://2nxsg.csb.app/

friendly-noyce-2nxsg
nas5w

309 0 1

Edit Sandbox

Files

.codesandbox
src
Open Sandbox
index.js
styles.css
Console 0 Problems 0
index html

Conclusion
Thanks for playing with shapes, states, and even a little math with me today. Hopefully
you learned something new today and how have a handy utility to fit items in your SVGs.

Please give this post a 💓, 🦄, and 🔖 if you learned a little


something about SVGs!

Discussion Subscribe

Add to the discussion

Code of Conduct • Report abuse

Nick Scialli (he/him)

Husband, dog 4dad, software engineer, coffee


4 4 tech!
monster. Working in civic
Follow

WORK
Software Engineer at USDS
LOCATION
Washington, DC

JOINED
Feb 8, 2019

More from Nick Scialli (he/him)

Future You Will Never Think Current You Was Too Old to Learn Software Engineering
#programming #webdev #beginners #career

Generating Random Human-Readable Slugs in JavaScript


#javascript #typescript #webdev #node

Build a useLocalStorage React Hook Package (Contribute to Open Source with Me)
#react #javascript #typescript #webdev

4 4 4

You might also like