Consider the following JavaScript snippet:
const app = document.getElementById(\'root\');
const svg = `
The problem here is that data: URLs are treated as having a unique origin that differs from the origin of the context that created the embedded data:
context:
Note: Data URLs are treated as unique opaque origins by modern browsers, rather than inheriting the origin of the settings object responsible for the navigation.
The WHATWG specification describes how content documents are accessed, which includes a cross origin check. The WHATWG same-origin comparison will never treat a traditional scheme-host-port "tuple" origin as equal to an "opaque" data:
origin.
Instead, use Blob
with URL.createObjectURL
to generate a same-origin temporary URL whose contents will be readable by the outer environment:
var svgUrl = URL.createObjectURL(new Blob([svg], {'type':'image/svg+xml'}));
obj.setAttribute('data', svgUrl);
I don't know the security reason why this approach is allowed while a raw data:
URL is not, but it does appear to work. (I guess because the generated URL is readable only by the origin that generated it, whereas a data:
URL doesn't know how to be readable only by the original of its originating context.)
Note also that some versions of Internet Explorer support createObjectURL
but erroneously treat the generated URLs as having a null origin, which would cause this approach to fail.
Other options are:
Don't use a data:
URL and instead serve the SVG content from the same origin as your page that creates the element.
Ditch the and
contentDocument
altogether and use an inline
const obj = document.createElement('div');
obj.innerHTML = svg;
app.appendChild(obj);
setTimeout(() => {
console.log(obj.querySelector('svg'));
}, 1500);
Most browsers support inline elements (notably, IE 9.0+; other browsers much earlier). This means you can do
and it will just render the SVG document inside the Depending on what you want to do with the SVG, you can load it into a DOMParser and do DOM exploration/manipulation within the parser. But the DOM model will be separate from the SVG rendered in the This doesn't seem super performant, because it needs the browser to re-parse a brand-new SVG document, but it will get around the security restrictions. Think of the However, if you want to make components within the SVG interactive by attaching listeners to components within the SVG structure that execute code on your main page, I don't think this approach will work. The separation between an var oParser = new DOMParser();
var svgDOM = oParser.parseFromString(svg, "text/xml");
console.log(svgDOM.documentElement.querySelector('path'));
svgDOM.documentElement.querySelector('path').remove();
. To change the
, you need to serialize the parsed DOM structure and re-push it to the the
data
property:var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(svgDOM);
obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(sXML)}`);
as a one-way black hole that can receive SVG information to render, but will not expose any information back. This isn't an informatic problem, though, since you have the information that you just fed into the
: there's nothing that
contentDocument
can tell you that you don't already know. and its surrounding page has the same kind of embedding relationship as an
.