Write JavaScript like a pro. Javascript Icon

Follow the ultimate JavaScript roadmap.

Hacking SVG, traversing with ease - addClass, removeClass, toggleClass functions

Update: I’ve turned this into a small JavaScript module named Lunar and pushed to GitHub, please use that code instead as it doesn’t extend native DOM APIs and also has accompanying unit tests :)


I encountered how painful traversing inline SVG can be when working on a recent project, simple DOM APIs such as adding, removing and toggling classes just aren’t there, or supported by tools such as jQuery (yes, I even tried jQuery).

Inline SVG is SVG in the DOM, rendered from its XML. Here’s a quick look at example inline SVG which would sit anywhere in the DOM:

<svg id="svg" xmlns="https://www.w3.org/2000/svg" version="1.1" height="190">
  <circle cx="100" cy="50" r="40" fill="red" />
</svg>

The svg element acts as a wrapper to the XML inside, whilst defining a few things such as height, width, namespace and version. You’ll notice I’ve added an id attribute, with the value of svg. Current DOM APIs make this seamlessly easy to target:

// grabs 
var mySVG = document.querySelector('#svg');

Problem: DOM stuff

But the problem begins when trying to do the ‘usual’ DOM stuff, adding a class, or removing a class. You’d think it would be pretty simple, but even using jQuery’s APIs don’t allow it to work either, so I wrote my own and I’m pretty pleased with its compactness. The trick is to set the attribute again, you can’t keep adding/removing classes using the .className method. The getAttribute method is what I’ve used to grab the class attribute’s value, and then the idea behind it is to grab that attribute, manipulate it and then set it back again.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

Let’s say I have a function, I need to add a class to an SVG circle onclick:

// grabs 
var mySVG = document.querySelector('#svg circle');
mySVG.setAttribute('class', 'myClass');

… will give us:

  <svg id="svg" xmlns="https://www.w3.org/2000/svg" version="1.1" height="190">
  <circle cx="100" cy="50" r="40" fill="red" class="myClass" />
</svg>

I have to think as the ‘class’ attribute as totally made up and that className doesn’t exist. And manipulating this is where the magic happens.

hasClass API

As with all the APIs, I’m hanging these off the SVGElement’s prototype constructor so all SVG nodes inherit the methods. With hasClass, I’m extending the native Object with a function. This method allows simple declaration of the APIs. Inside the hasClass function, I’m creating a new Regular Expression, which gets dynamically created through its className parameter and immediately tested against its attribute value. JavaScript’s .test() returns a boolean (true/false), a simple way to test a class presence.

SVGElement.prototype.hasClass = function (className) {
  return new RegExp('(\\s|^)' + className + '(\\s|$)').test(this.getAttribute('class'));
};

addClass API

Adding a class is simple, just set the attribute. Here I simply make a check using the hasClass API, and if it doesn’t exist I add it. There’s no point adding it again if it does exist. If it doesn’t exist, I set the attribute class with the current class value, plus my new class name, super simple.

SVGElement.prototype.addClass = function (className) {
  if (!this.hasClass(className)) {
    this.setAttribute('class', this.getAttribute('class') + ' ' + className);
  }
};

removeClass API

Removing a class was the most fun, there’s also the issue of keeping spaces intact, for instances I had to work out how to remove a class and keep the appropriate spaces around that class name. You can see I create a new class here called removedClass, where I get the current value, then replace the passed in className using a dynamically created RegExp again. This RegExp has some coolness added to it, you’ll see I declare ‘g’ at the end of the RegExp declaration, this means global, and will replace all instances of the class, for example if it was declared more than once throughout the class value. I then make a safety check to ensure the class is there, and set the attribute back on the element.

You’ll also see I used a second parameter as well in the replace method, which says ‘$2’. This is a nifty little trick, which refers to the capture group in the RegExp. Capture groups are denoted by circular brackets, my example at the end of the RegExp says _’(\s $)’, this denotes a capture group, and then looks for whitespace after the className, OR it’s at the end of the string, which is what the _$ means. I can then remove the className and leave whatever was in the capture group, either a space or nothing, which keeps the class value tidy.
SVGElement.prototype.removeClass = function (className) {
  var removedClass = this.getAttribute('class').replace(new RegExp('(\\s|^)' + className + '(\\s|$)', 'g'), '$2');
  if (this.hasClass(className)) {
    this.setAttribute('class', removedClass);
  }
};

toggleClass API

Toggling from hereon is super simple, I’ll check if the element has the class, and based on that I’ll either add or remove the class using the above APIs.

SVGElement.prototype.toggleClass = function (className) {
  if (this.hasClass(className)) {
    this.removeClass(className);
  } else {
    this.addClass(className);
  }
};

Usage

Usage is simple, and simple API style:

// Grab my Node
var mySVG = document.querySelector('#svg circle');

// hasClass
mySVG.hasClass('zzz');

// addClass
mySVG.addClass('zzz');

// removeClass
mySVG.removeClass('zzz');

// toggleClass
mySVG.toggleClass('zzz');

Thank you for reading!

Learn JavaScript the right way.

The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover