gpm*_*dam 18 html javascript dom cors
请考虑以下JavaScript代码段:
const app = document.getElementById('root');
const svg = `<svg version="1.1" id="Layer_1"...`;
const obj = document.createElement('object');
obj.setAttribute('type', 'image/svg+xml');
obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(svg)}`);
app.appendChild(obj);
setTimeout(() => {
console.log(obj.contentDocument.querySelector('svg'));
}, 1500);
Run Code Online (Sandbox Code Playgroud)
(有关完整示例,请参阅此JSFiddle)
运行时,控制台(Google Chrome)中会出现以下错误:
未捕获的DOMException:无法从'HTMLObjectElement'读取'contentDocument'属性:阻止具有源" https://fiddle.jshell.net " 的帧访问跨源帧.at setTimeout(https://fiddle.jshell.net/_display:77:19)
考虑到这一点;
当尝试访问contentDocument完全动态创建的对象而没有外部资源时,为什么这被视为跨源请求?
有没有办法以这种方式动态生成SVG,而不会冒犯浏览器的跨源策略?
aps*_*ers 14
这里的问题是data:URL被视为具有与创建嵌入data:上下文的上下文的原点不同的唯一来源:
注意:现代浏览器将数据URL视为唯一的不透明原点,而不是继承负责导航的设置对象的来源.
WHATWG规范描述了如何访问内容文档,其中包括跨源检查.在WHATWG同源比较绝不会像传统方案主机端口"元组"起源等于"不透明" data:的起源.
相反,使用Blobwith URL.createObjectURL生成同源的临时URL,其内容可由外部环境读取:
var svgUrl = URL.createObjectURL(new Blob([svg], {'type':'image/svg+xml'}));
obj.setAttribute('data', svgUrl);
Run Code Online (Sandbox Code Playgroud)
我不知道原始data:URL 不允许这种方法的安全原因,但它似乎确实有效.(我想因为生成的URL只能由生成它的原点读取,而data:URL不知道如何只能通过其原始上下文的原始读取.)
另请注意,某些版本的Internet Explorer支持createObjectURL但错误地将生成的URL视为具有空原点,这会导致此方法失败.
其他选择是:
不要使用data:URL,而是使用与创建<object>元素的页面相同的源来提供SVG内容.
抛弃<object>并contentDocument完全使用内联<svg>元素(小提琴):
const obj = document.createElement('div');
obj.innerHTML = svg;
app.appendChild(obj);
setTimeout(() => {
console.log(obj.querySelector('svg'));
}, 1500);
Run Code Online (Sandbox Code Playgroud)
大多数浏览器都支持内联<svg>元素(特别是IE 9.0+;其他浏览器更早).这意味着你可以做到
<div>
<svg>
...
</svg>
</div>
Run Code Online (Sandbox Code Playgroud)
它只会<div>像你期望的那样将SVG文档呈现在内部.
根据您要对SVG执行的操作,您可以将其加载到DOMParser解析器中并进行DOM探索/操作.
var oParser = new DOMParser();
var svgDOM = oParser.parseFromString(svg, "text/xml");
console.log(svgDOM.documentElement.querySelector('path'));
svgDOM.documentElement.querySelector('path').remove();
Run Code Online (Sandbox Code Playgroud)
但是DOM模型将与在中呈现的SVG分开<object>.要更改<object>,需要序列化已解析的DOM结构并将其重新推送到data属性:
var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(svgDOM);
obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(sXML)}`);
Run Code Online (Sandbox Code Playgroud)
这似乎不是超级高效的,因为它需要浏览器重新解析一个全新的SVG文档,但它将绕过安全限制.
可以将其<object>视为可以接收SVG信息进行渲染的单向黑洞,但不会将任何信息暴露回来.然而,这不是一个信息问题,因为你得到的信息只是你所提供的<object>:没有任何东西contentDocument可以告诉你,你还不知道.
但是,如果您想通过将侦听器附加到主页上执行代码的SVG结构中的组件来在SVG交互中创建组件,我认为这种方法不起作用.a <object>与其周围页面之间的分离具有与之相同的嵌入关系<iframe>.