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.
Doesn’t work at all in Opera.
Really? It seemes like it does.
Wrote up something like this a month or so ago. I rely on jQuery, but you could obviously do it with vanilla JS. https://pathartl.me/inline-svg-ajax/
Pretty cool!
That looks like it does an ajax request for every single SVG. The idea of a SVG sprite is that it’s a single request. Just something to consider.
Yep! I wrote this mainly for use with large SVG’s. Or rather loading CSS-able SVG’s like normal images are loaded. Hence loading them synchronously.
Just a tad confused. So you’re saying that using Ajax that the browser is able to cache sprite.svg because the browser treats it as a file yes?
Also, you’re of course relying on JS to work. If it fails or disabled obviously this wouldn’t work, but I guess that’s fine if you’re only including enhancements like icons.
That’s all correct.
For instance, here’s ensuring the SVG is served correctly and cached from .htaccess:
I like it!
I’ve been using something similar to load svg combined by gulp-svgstore. The only difference is that I insert content before the script tag.
Thanks for the shout-out about the
innerHTML
trick, but you don’t need it here.XMLHttpRequest
provides you with a complete XML document; you can just grab the SVG node and append it directly.(Because a valid stand-alone SVG file is XML, this will have better browser support than for HTML documents, which were a later extension.)
The only trick is that for Firefox and IE you need to explicitly tell them that the response will be a document, not plain text/binary data. I would think the
image/svg+xml
MIME type would be enough, but apparently not.The bare-bones JavaScript code would then be:
I add a couple extra lines in this pen, to set a class on the SVG before I inject it into the document and to add a try/catch block just in case there are file access problems. It’s working if you see a shiny gold rectangle, it’s not if you see solid red:
Thanks, Amelia. Could you help me sort out the best way to fetch and load multiple svg sprite files with AJAX?
I’m surprised to see this works using an asynchronous request. What happens if
<use>
is parsed before the SVG is added to the page?Love it !
Love it!
can make it just with html 5 ?
Well, Its tricky but useful!
I just put
<object>
into html:And then replace it with the SVG content once it’s loaded:
You get caching & clean markup this way IMO.
I wrote a helper to do this replacements for me like this:
That looks neat, but does that mean that every single SVG is separate? The larger idea here is single-request.
I used this technique to load few separate files, but it shouldn’t matter really. You can load a sprite and then use it like you want.
HTML:
JS:
Just did a quick test – seems to work fine in all browsers (FF, Chrome, Safari, Opera, IE 9 – 11).
I just stumbled over a nasty quirk with this technique: wherever they’re placed, Chrome (40) sees used symbols (e.g.,
<use xlink:href="#symbol-id" />
) as descendants of the original<svg>
tag, including that tag’s position in the DOM, which eliminates the possibility of re-styling the same SVG symbol with different fills depending on the context theuse
tag appears in.e.g., this won’t work in Chrome:
SVG:
HTML:
CSS:
Works in Firefox, and I haven’t tried it in other browsers, but Chrome doesn’t consider the symbol to actually exist within the
<use>
tag so the selector.pink-logo .logo-part
is effectively unmatched.Minor follow-up; looks like Chrome’s behavior in accord with W3C spec:
http://www.w3.org/TR/SVG11/struct.html#UseElement
Since this applies all the way down to the SVG’s internals, the only workaround I can think of (for situations where you need to select & style specific elements within an SVG) is to use JS to pick symbols out of the DOM and append them directly to a new SVG element, avoiding
<use>
altogether. For most situations, you’re almost certainly better off just creating duplicate symbols for each of your variations. (Cue sad trombone.)