How to create svg elements with Javascript

How to create svg elements with Javascript:

In a nutshell: What are SVGs?

If you’ve ever taken a small image and tried to scale it up in size, you know the struggle: It gets pixelated and the fonts become an unreadable raster of black-to-white’ish squares. Fortunately, there are resolutions to the matter, one of which has been standardized within the .svg file format. While other common formats, such as

.png, are based on a grid of pixels, svgs consist out of a fixed set of shapes. The way these are drawn and aligned is described with XML – markup, more specifically with paths. This allows for a more dynamic scaling.

Yug, modifications by 3247CC BY-SA 2.5, via Wikimedia Commons

In a nutshell, raw SVG files in the wilderness:

  • are namespaced within their xml namespace (xmlns) – standard.
  • contain one or several paths within the – tags that make up the actual graphc.
  • can be styled with css and inline styles.

Consider this example from Heroicons. If you drop the markup into an html file, it will render into the actual icon.


  

(note: I replaced the tailwind classes with a style attribute, the result is about the same though)

Now that you’ve got a glimpse about the format, you might already have an idea how the post’s topic is to be solved – by means of DOM – manipulation. So let’s try and recreate the element above with Javascript.

Dynamic XML-node creation – boilerplate

XML differs from HTML in several aspects, the most relevant being that XML does not have predefined tags. Instead, it allows you to define these yourself within so-called namespaces.

This also allows for dynamically adding SVG icons to data from a remote location you’d like to bind to a client’s interface while – or after – the data is being rendered. Let’s assume you run a blog and would like to dynamically add the ‘link’-icon from above before every post’s heading. For a user’s convenience, we’ll add an anchor tag that permits the reader to scroll this post directly scroll it into their center of attention. To illustrate this example, let’s start with the following boilerplate:

  • We use a simple index.html file that holds a list of posts.
  • These posts are fetched from jsonplaceholder and dynamically added to the DOM by a function inside the main.js file.
  • main.css provides us a few basic styles for our list.

So launch your favorite text editor and add them to a free directory of your choice.

index.html




  
  
  
  
  Create SVGs with Javascript - Demo


  

Posts from today

http://main.js

main.js

async function getPostData() {
  const url = 'https://jsonplaceholder.typicode.com/posts';
  const response = await fetch(url);
  return await response.json();
}

function renderPosts(app, posts) {
  const postNodes = posts.map((post) => {
    // Create the DOM elements
    const postCard = document.createElement('div');
    const postHeader = document.createElement('div');
    const postTitleAnchor = document.createElement('a');
    const postTitle = document.createElement('h2');
    const postText = document.createElement('p');

    // Add some classes and attributes
    postCard.classList.add('post-card');
    postHeader.classList.add('post-header');
    postTitle.classList.add('post-title')
    postTitle.id = post.title;
    postTitleAnchor.href = '#' + post.title;

    // Place the text content
    postTitle.textContent = post.title;
    postText.textContent = post.body;

    // TODO: Add the icon here

    // Put together the DOM nodes
    postHeader.appendChild(postTitleAnchor)
    postHeader.appendChild(postTitle);
    postCard.appendChild(postHeader);
    postCard.appendChild(postText);
    app.appendChild(postCard);

    return postCard;
  });
  return postNodes;
}

async function mountPosts() {
  const app = document.querySelector('#posts');
  const posts = await getPostData();
  renderPosts(app, posts);
}

mountPosts();

main.css

* {
  scroll-behavior: smooth;
}

body {
  font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
  background-color: blueviolet;
  margin: 0;
  padding: 0;
}

h1 {
  padding: 2rem 0;
  margin: 0;
}

.site-header {
  position: sticky;
  text-align: center;
  width: 100%;
  background-color: #fff;
}

.post-list {
  padding: 0 20vw;
}

.post-card {
  border-radius: 2rem;
  background-color: #fff;
  padding: 1rem 2rem;
  margin: 2rem;
}

.post-icon {
  transition: 0.25s all;
  border-radius: 0.25rem;
  height: 2rem;
  width: 2rem;
  margin-right: 0.5rem;
  padding: 0.25rem;
}

.post-icon:hover {
  transition: 0.5s all;
  background-color: blueviolet;
  stroke: white;
}

.post-header {
  display: flex;
  align-items: center;
}

@media only screen and (max-width: 1200px) {
  .post-list {
    padding: 0 10vw;
  }
}

@media only screen and (max-width: 600px) {
  .post-list {
    padding: 0 2vw;
  }
}

You’ll receive a UI that looks like this, a simple and clean post collection.

Add a function to create the XML

Let’s take a look into the xml-file again:


  

  • It has a tag as a wrapper which includes the namespace and some attributes.
  • Within, there’s one (or several) tags that describes the shape of the SVG.
  • Inside the browser’s context, both of these are interpreted and rendered like html-tags.

The last point also implies that said xml-tags can be created and composed like html-elements. An tag, for instance, can be created like this:

// Create an element within the svg - namespace (NS)
document.createElementNS('http://www.w3.org/2000/svg', 'svg');

From then on, the svg can be mostly be handled like any other element. You can add styles, classes and also – most importantly – attributes.

So let’s add the following function to the main.js file. It will take in the anchor tag into which we will inject the created graphic, making it suitable for our scrolling feature.

function renderLinkIcon(node) {
  const iconSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  const iconPath = document.createElementNS(
    'http://www.w3.org/2000/svg',
    'path'
  );

  iconSvg.setAttribute('fill', 'none');
  iconSvg.setAttribute('viewBox', '0 0 24 24');
  iconSvg.setAttribute('stroke', 'black');
  iconSvg.classList.add('post-icon');

  iconPath.setAttribute(
    'd',
    'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1'
  );
  iconPath.setAttribute('stroke-linecap', 'round');
  iconPath.setAttribute('stroke-linejoin', 'round');
  iconPath.setAttribute('stroke-width', '2');

  iconSvg.appendChild(iconPath);

  return node.appendChild(iconSvg);
}

Making it all functional

Now that we have all building blocks in place that adds the icon, let’s put it to action.

Add the following inside the main.js file, right after placing the text-content:

// TODO: Add the icon function here
renderLinkIcon(postTitleAnchor);

And that’s it. The icons are prepended to each post and can easily be used as anchor links for smooth scrolling. Below goes the final result:

from Tumblr https://generouspiratequeen.tumblr.com/post/647815936344260608

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s