Passing properties to a hosted SVG

The problem

By embedding an SVG inline you can access its DOM and fiddle with its CSS. When it's loaded in via an image tag, it exists on its own namespace so you cannot affect its internal CSS.

The solution

We can add the properties we want to change using the URL, and use Javascript to catch them and update things inside the SVG.

Step 1 - Create the SVG

Here's a simple SVG.

<svg viewBox="0 0 100 100" width="100">
  <circle cx="50" cy="50" r="50" class="outer-circle" fill="#88aa44"></circle>
  <circle cx="50" cy="50" r="30" class="inner-circle" fill="#7aaeea"></circle>
</svg>
A couple of circles

Step 2 - Add the Javascript to the SVG

Yeah you can put Javascript inside SVGs. I've stuck a script tag in there that changes the styling of things in its DOM. Here I'm changing the colour of the inner circle to a sexy grey.

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="100">
  <circle cx="50" cy="50" r="50" class="outer-circle" fill="#88aa44"></circle>
  <circle cx="50" cy="50" r="30" class="inner-circle" fill="#7aaeea"></circle>
  <script>
    // <![CDATA[
    window.addEventListener('DOMContentLoaded', () => {
      const outer = document.querySelector('.outer-circle');
      const inner = document.querySelector('.inner-circle');

      inner.style.fill = "#333";
    });
    // ]]>
  </script>
</svg>

This is saved and uploaded to somewhere. If we want the Javascript to be run then we can't use <img> to load the image but we can load it and run the script by using an <object> element.

<object type="image/svg+xml" data="/img/2022/06/pptis-img1.svg" aria-label="SVG with internal script changing the inner circle colour">
  <img alt="SVG with internal script changing the inner circle colour" src="/img/2022/06/pptis-img1.svg" />  <!-- Fallback -->
</object>

Which gives us this:

SVG with internal script changing the inner circle colour

The inner circle has now gone grey thanks to a bit of Javascript.

Step 3 - pass parameters

Because the SVG has its own DOM, it has its own window which means it also has its own window.location; it knows the path it was loaded from. And using querystring parameters, we have gateway between the hosting page and the SVG code.

I'm going to add a couple of parameters to the querystring of the path to the the SVG, with hex values on them.

<object type="image/svg+xml" data="/img/2022/06/pptis-img1.svg?inner=985588&outer=aa88ee" ...>

And in the SVG, add some code to grab the value of these parameters and apply them to the elements in the SVG. It's looking for those parameters in the querystring, inner and outer.

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="100">
  <title>A couple of circles</title>
  <circle cx="50" cy="50" r="50" class="outer-circle" fill="#88aa44"></circle>
  <circle cx="50" cy="50" r="30" class="inner-circle" fill="#7aaeea"></circle>
  <script>
    // <![CDATA[
    window.addEventListener('DOMContentLoaded', () => {
      const outer = document.querySelector('.outer-circle');
      const inner = document.querySelector('.inner-circle');

      let url = (new URL(window.location)); // Get the URL
      const innerColour = url.searchParams.get('inner'); // Look for the querystring parameters
      const outerColour = url.searchParams.get('outer');

      outer.style.fill = `#${outerColour}`; // Update the CSS of the circle elements
      inner.style.fill = `#${innerColour}`;
    });
    // ]]>
  </script>
</svg>

The results

Now when we can pass parameters to our SVG, which can use them to do stuff with its contents:

?inner=aa5588&outer=aa88ee

?inner=334455&outer=a0f9af

?inner=cdcdcd&outer=898989

?inner=red&outer=pink