You can create an svg element using the createElementNS method.
document.createElementNS(<namespace>, <element name>)
You may be familiar with the similarly named createElement method, used to create HTML elements. Why can’t we just use that? Because the SVG “namespace” isn’t the same as the HTML namespace. If you try to create an svg “circle” element with createElement, the browser won’t know what to do with it because it thinks it’s just an unknown html element. By passing the SVG namespace, you’re telling the browser that you want to create an SVG element, and NOT an HTML element.
namespace = "http://www.w3.org/2000/svg"
So let's create a circle element and add it to our practice page
First we'll create the circle element
1
2
const namespace = "http://www.w3.org/2000/svg";
let my_circle = document.createElementNS(namespace, "circle");
Next, we'll add the presentation attributes to style the circle using setAttribute.
1
2
3
4
5
6
7
const namespace = "http://www.w3.org/2000/svg"
let my_circle = document.createElementNS(namespace, "circle");
my_circle.setAttribute('cx', 150);
my_circle.setAttribute('cy', 150);
my_circle.setAttribute('r', 100);
my_circle.setAttribute('fill', purple);
my_circle.setAttribute('stroke', none);
The SVG is still blank! We still need to append the circle to the svg using appendChild:
element.appendChild(<another_element>)
1
2
3
4
5
6
7
8
const namespace = "http://www.w3.org/2000/svg"
let my_circle = document.createElementNS(namespace, "circle");
my_circle.setAttribute('cx', 150);
my_circle.setAttribute('cy', 150);
my_circle.setAttribute('r', 100);
my_circle.setAttribute('fill', purple);
my_circle.setAttribute('stroke', none);
my_svg.appendChild(my_circle)
Creating simple shapes and paths is easy enough, but what if you want to create something a bit more complex? One way to do so is to replicate objects defined in the defs section.
For example suppose you created a leaf graphic in Figma, and you wanted to create multiple copies of it.
I've created a practice page with a sample leaf graphic defined in the defs section, and added it to a "leafholder" group.
1
2
3
4
5
6
7
8
9
<svg width="500" height="500" viewBox="0 0 500 500">
<defs>
<g id="leaf">
<path id="surface" fill="#06B943" d="M28.817 0C6.04933 16.1538 -22.6636 70 28.817 70C85.8145 70 51.5847 30.5128 28.817 0Z" />
... more leaf code ...
</g>
</defs>
<g id="leafHolder"></g>
</svg>
To place the leaf dynamically, we can create the <use> element and pass in the leaf id as the href value. We'll need variables for the svg namespace, the leaf definition (in defs) and the leafHolder:
1
2
3
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
Next, we'll create a <use> element and pass in the id of the leaf graphic in <defs>
let my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute('href', '#leaf');
1
2
3
4
5
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
let my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute('href', '#leaf');
Finally, we'll append the <use> element to the svg:
element.appendChild(<element>);
1
2
3
4
5
6
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
let my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute('href', '#leaf');
leafHolder.appendChild(my_leaf);
We can change the leaf’s position by adding x and y values.
1
2
3
4
5
6
7
8
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
let my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute('href', '#leaf');
my_leaf.setAttribute("x", 220);
my_leaf.setAttribute("y", 220);
leafHolder.appendChild(my_leaf);
Instead of just placing one leaf, let’s use a loop to create a bunch of leaves and position them randomly. We'll create random x and y positions using Math.random() (We'll multiply Math.random() x 500 to make sure we have x and y points that fit inside the 500x500 svg).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
function buildLeaves() {
const num = 80;
for (let i = 0; i!=num; i++) {
const my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute("href", "#leaf");
const xpos = String(Math.random() * 500); // value between 0-500
const ypos = String(Math.random() * 500); // value between 0-500
my_leaf.setAttribute("x", xpos);
my_leaf.setAttribute("y", ypos);
leafHolder.appendChild(my_leaf);
}
};
Now we’ll rotate the leaves to make for a more realistic scatter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
function buildLeaves() {
const num = 80;
for (let i = 0; i!=num; i++) {
const my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute("href", "#leaf");
const xpos = String(Math.random() * 500); // value between 0-500
const ypos = String(Math.random() * 500); // value between 0-500
const angle = Math.random() * 180;
const transformString = `rotate(${angle} ${xpos} ${ypos})`;
my_leaf.setAttribute("x", xpos);
my_leaf.setAttribute("y", ypos);
my_leaf.setAttribute("transform, transformString)
leafHolder.appendChild(my_leaf);
}
};
What if we wanted to change the color of each leaf? Let' do that by setting each leaf element's fill.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const namespace = "http://www.w3.org/2000/svg";
const leafDef = document.querySelector("#leaf");
const leafHolder = document.querySelector("#leafHolder");
function buildLeaves() {
const num = 80;
for (let i = 0; i!=num; i++) {
const my_leaf = document.createElementNS(namespace, "use");
my_leaf.setAttribute("href", "#leaf");
const xpos = String(Math.random() * 500); // value between 0-500
const ypos = String(Math.random() * 500); // value between 0-500
const angle = Math.random() * 180;
const transformString = `rotate(${angle} ${xpos} ${ypos})`;
my_leaf.setAttribute("x", xpos);
my_leaf.setAttribute("y", ypos);
my_leaf.setAttribute("transform, transformString);
my_leaf.setAttribute("fill, "orange");
leafHolder.appendChild(my_leaf);
}
};
When we try changing the color of each leaf from green to orange by setting the fill, nothing happens. Why?
The “surface” path (which defines the leaf surface) already has a fill attribute defined. Remember from our lesson on groups - presentation attributes defined on child elements override those set on the group. So setting a fill of orange on the group would have no effect on the child "surface" path.
<g id="leaf" fill="orange">
<path id="surface" ... fill="#06B943"/>
If we want to set the fill dynamically on the group level, we need to delete the fill defined on the surface path
<g id="leaf" fill="orange">
<path id="surface" ... />
Just for fun, let’s set the leaves to random colors. I've created a getColor() function that returns an hsl color string we can use with the setAttribute method to set the fill color.
1
2
3
4
5
6
7
8
const getColor = () => {
const h = Math.round(Math.random() * 360);
const s = 50 + Math.round(Math.random() * 50);
const l = 40 + Math.round(Math.random() * 50);
return `hsl(${h}, ${s}%, ${l}%)`;
}
1
2
3
4
5
for (let i = 0; i !=; i++) {
// ...
my_leaf.setAttribute("fill", getColor());
// ...
}
Removing elements is even easier. Just use the DOM’s element.remove() method. Let’s add a click event handler to each leaf to do just that.
1
2
3
4
5
6
7
for (let i = 0; i != num; i++) {
// ...
my_leaf.addEventListener("click", () => {
my_leaf.remove();
});
// ...
}
You can check out the final version in this codepen example: