There are other solutions, but AJAX is indeed the simplest if you plan in appending the SVG in your document.
But, beware this may not be a great idea.
As you probably know, SVG are documents, and their nodes can have id
attributes. id
attributes must be unique per document, so if you append twice the same svg document containing nodes with id
, you will have duplicate ids and probably problems.
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<defs>
<rect id="rect1" x="0" y="0" width="50" height="50" fill="red"/>
</defs>
<use xlink:href="#rect1" stroke="green"/>
</svg>`;
function appendSVGDoc(markup, parent) {
const doc = (new DOMParser).parseFromString(markup, 'image/svg+xml');
const docEl = document.adoptNode(doc.documentElement); parent.appendChild(docEl);
}
// append a first version
appendSVGDoc(svg, document.body);
onclick = e => {
// let's do some modifs
document.getElementById('rect1').setAttribute('fill', 'yellow');
onclick = e => {
// append an other one
appendSVGDoc(svg, document.body);
// guess what color is the rect here?
};
};
<p>click to modify the svg element.</p>
<p>click again to add a new copy of the original svg</p>
An other example? If your svg document contains a stylesheet, then the rules of this stylesheet will impact your whole document:
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="200">
<style>
:root {
font: 40px Serif;
}
rect {
fill: green;
}
</style>
<text y="50">I'm a big text in an svg image.</text>
<rect x="25" y="75" width="50" height="50"/>
</svg>`;
function appendSVGDoc(markup, parent) {
const doc = (new DOMParser).parseFromString(markup, 'image/svg+xml');
const docEl = document.adoptNode(doc.documentElement); parent.appendChild(docEl);
}
onclick = e => {
appendSVGDoc(svg, document.body);
};
<p>I am a simple html text content, and here is a simple svg <rect> that should be red: <svg width="50" height="50"><rect x="0" y="0" height="50" width="50" fill="red"/></svg></p>
<p> click to append the external SVG </p>
So to avoid such cases, I can only advise you actually load your svg docs in <iframe>. From there, you will be able to access their content-document and do the edits as you wish, while keeping their document sand-boxed from your main document.
Note: <object> or <embed> seem more logical elements than <iframe>, but, for a reason still unknown by me, no browser does use the cache when fetching resources for such elements. That's pretty bad...
However, while <iframe> do use cache, they don't resize dynamically, and they come with ugly borders...
function appendSVGDoc(blob, parent) {
return new Promise((res, rej) => {
const url = URL.createObjectURL(blob);
const iframe = document.createElement('iframe');
iframe.classList.add('svg-viewer');
iframe.onload = e => {
const doc = iframe.contentDocument;
const docEl = doc.documentElement;
// iframe doesn't auto size like <object> would
iframe.width = docEl.getAttribute('width') || '100%';
iframe.height = docEl.getAttribute('height') || '100%';
res(doc);
};
iframe.onerror = rej;
iframe.src = url;
parent.appendChild(iframe);
});
}
fetch(url)
.then(resp => resp.blob())
.then(blob => appendSVGDoc(blob, document.body));
.then(svgdoc => {
// here manipulate the svg document
})
.catch(console.error);
StackSnippets® overly protected (null-origined) own blocking us from accessing inner-iframes content, I had to out-source this last example to jsfiddle.