Ajaxing for your SVG Sprite

Avatar of Chris Coyier
Chris Coyier on

Say you’re building an SVG icon system. You’re building a SVG sprite full of symbols by hand, or using a build tool like IcoMoon or grunt-svgstore to create it for you.

What do you do with that sprite.svg?

One option is to include it right at the top of the document and then <use> away.

...

</head>

<body>

  <!-- include it here -->
  <?php include_once("svg/sprite.svg"); ?>

  ...

  <!-- use it here -->
  <a href="/" class="logo">
    <svg class="logo">
      <use xlink:href="#logo" />
    </svg>
  </a>

That works, but it doesn’t make very good use of caching. If the site does HTML caching, every single page will have this big chunk of identical SVG in it, which is a bit bloaty. Not to mention the HTML parser will need to read through all of that in the document each page load before moving on to the content.

Probably better to take advantage of browser caching, like we do all other assets. We can do that by making our <use> link to an external source. But some browsers have trouble with that. Namely any version of IE and some older WebKit stuff.

SVG for Everybody, that we recommended here, mostly works well. But, there are some browsers it doesn’t catch with the UserAgent sniff it does.

An alternative approach is just to ajax for the sprite (all the time) and inject it onto the page. That means you can browser cache that SVG, and it works everywhere that inline SVG works.

Assuming you have a complete SVG document you’re ajaxing for (sprite.svg), it’s actually slightly tricky. You have to ensure that the SVG is in the proper namespace before you append it to the page (thanks Amelia). Luckily we can exploit the HTML parser that normally is in charge of this. We do this by appending the SVG into a <div> then appending that to the page.

var ajax = new XMLHttpRequest();
ajax.open("GET", "svg/sprite.svg", true);
ajax.send();
ajax.onload = function(e) {
  var div = document.createElement("div");
  div.innerHTML = ajax.responseText;
  document.body.insertBefore(div, document.body.childNodes[0]);
}

If you’re using jQuery, the callback data it gives you is already formatted into an SVG document, so you need to force it back into a string before appending it to the div and ultimately the page.

$.get("svg/sprite.svg", function(data) {
  var div = document.createElement("div");
  div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
  document.body.insertBefore(div, document.body.childNodes[0]);
});

Remember that you use with just the identifier when you go this way, not the external source.

<svg class="icon" viewBox="0 0 100 100">
  <use xlink:href="#shape-icon-1" />
</svg>

Seems to work well in IE and Android:

Also remember that SVG for Everybody helps you with transforming the markup into <img src="...png"> in non-supporting browsers, so if that’s important to you, you’d be on your own there.