Scaling SVG with JavaScript
Tags: javascript
Published: 2021-01-24 03:23:29

I recently decided to add a mode to a graphing module I've been working on which allows the graph to be drawn using either SVG line or path elements. The graph will automatically scale to its container, and render each line appropriately given the width and height of the container. Until this point, I had resolved this with a canvas, but I wanted to add an option to render the graph using SVG, as it typically looks cleaner.

I had worked minimally with SVG until this point, and quickly realized that things weren't going to work quite as easily as I initially anticipated. SVG is an XML-based markup, meaning that SVG elements aren't going to be created/manipulated in the same way that HTML elements would. Luckily, there are some useful methods which allow us to create and set the attributes of XML elements in JavaScript.

We can create HTML elements in JavaScript using `document.createElement`, but we can also create XML elements using `document.createElementNS`. When we use `document.createElementNS`, we need to pass two arguments to the method; the first being a string which points to the location of the namespace, and the second being a string which identifies the type of element we wish to create. Therefor, if we want to create an SVG element using JavaScript, we would use `document.createElementNS("http://www.w3.org/2000/svg", "svg");`.

We can append the element to its container using `container.append`, just as we would with any other HTML element, but what if we want to adjust the attributes of the SVG element? We need to use another useful method - `setAttribute`; `setAttribute` takes two arguments, the first being a string which identifies the attribute we want to set, and the second being the value.

The following code will create an SVG element, scale it to 100%, and append it to the container.

```javascript
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "100%");
svg.setAttribute("height", "100%");

const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("width", "100%");
rect.setAttribute("height", "100%");
rect.setAttribute("fill", "#fff0");

svg.append(rect);
document.querySelector(".container").append(svg);

...
```

We're also appending a rectangle to our SVG element at this point, so that we can draw something over it later on.

Now, we want to be able to draw something relative to the scale of the svg element, so we'll need to be able to get the width and height of the element after it scales. Similarly to setting the attributes of the SVG element, we need to learn about another method in order to get the value of an attribute. The method we need is `getBBox`; `getBBox` will return the bounding box of the element, and allow us to extract the values we need in order to scale our drawing accordingly.

With our SVG element appended to the container, we'll use `getBBox` to draw a red X which fills the dimensions of the SVG element.

```javascript
...

const svgBBox = svg.getBBox();
const width = svgBBox.width;
const height = svgBBox.height;

const line1 = document.createElementNS("http://www.w3.org/2000/svg", "line");
const line2 = document.createElementNS("http://www.w3.org/2000/svg", "line");

line1.setAttribute("x1", 0);
line1.setAttribute("y1", 0);
line1.setAttribute("x2", width);
line1.setAttribute("y2", height);
line1.setAttribute("fill", "red");

line2.setAttribute("x1", 0);
line2.setAttribute("y1", height);
line2.setAttribute("x2", width);
line2.setAttribute("y2", 0);
line2.setAttribute("fill", "red");

svg.append(line1);
svg.append(line2);
```

And that's all there is to it!

Below is a similar example using a series of buttons as containers, and if you're interested in the graphing module, you can find the repository [here](https://github.com/BrandonMWeaver/simple-chart); bear in mind that it's a work in progress, and I'm currently taking time to refactor.

{% codepen https://codepen.io/brandonmweaver/pen/BaLeEQz %}