В общем, проблема двукратная тройная:
HTML не является XHTML, а поддержка SVG в HTML не очень удобна и плохо определена на момент написания статьи. Решение состоит в том, чтобы использовать настоящий документ XHTML, в котором элементы пространства имен SVG фактически обрабатываются как SVG.
responseXML
находится в другом документе DOM, и вы не можете просто перемещать узлы из одного документа в другой. Вы должны использовать document.importNode
для импорта узла из одного документа в другой.
При загрузке файла SVG с помощью обработчиков событий onload
эти обработчики не будут вызваны ни созданием узла, ни добавлением его в документ. Однако код внутри блока script
будет выполнен, поэтому вам нужно переписать сценарии таким образом, чтобы он работал автономно, а также с динамической загрузкой.
Вот простой пример, который работает в Chrome, Safari и Firefox ... но не в IE9:
var xhr = new XMLHttpRequest;
xhr.open('get','stirling4.svg',true);
xhr.onreadystatechange = function(){
if (xhr.readyState != 4) return;
var svg = xhr.responseXML.documentElement;
svg = document.importNode(svg,true); // surprisingly optional in these browsers
document.body.appendChild(svg);
};
xhr.send();
Смотрите это в действии здесь: http://phrogz.net/SVG/import_svg.xhtml
К сожалению, IE9 не поддерживает должным образом document.importNode
. Чтобы обойти это, мы пишем нашу собственную функцию cloneToDoc
, которая создает эквивалентную структуру для любого данного узла путем рекурсивного сканирования иерархии. Вот полный рабочий пример:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="content-type" content="application/xhtml+xml;charset=utf-8"/>
<title>Fetch and Include SVG in XHTML</title>
<script type="text/ecmascript"><![CDATA[
setTimeout(function(){
var xhr = new XMLHttpRequest;
xhr.open('get','stirling4.svg',true);
xhr.onreadystatechange = function(){
if (xhr.readyState != 4) return;
var svg = cloneToDoc(xhr.responseXML.documentElement);
document.body.appendChild(svg);
};
xhr.send();
},1000);
function cloneToDoc(node,doc){
if (!doc) doc=document;
var clone = doc.createElementNS(node.namespaceURI,node.nodeName);
for (var i=0,len=node.attributes.length;i<len;++i){
var a = node.attributes[i];
if (/^xmlns\b/.test(a.nodeName)) continue; // IE can't create these
clone.setAttributeNS(a.namespaceURI,a.nodeName,a.nodeValue);
}
for (var i=0,len=node.childNodes.length;i<len;++i){
var c = node.childNodes[i];
clone.insertBefore(
c.nodeType==1 ? cloneToDoc(c,doc) : doc.createTextNode(c.nodeValue),
null
); }
return clone;
}
]]></script>
</head><body></body></html>
Смотрите это в действии здесь: http://phrogz.net/SVG/import_svg_ie9.xhtml
Редактировать 2: Как и предполагалось, проблема в том, что событие onload
не срабатывает при динамическом добавлении сценария. Вот парное решение, которое работает:
- Перепишите свой скрипт, чтобы удалить обработчик событий
onload
. Вместо этого верьте, что document
существует.
- Перепишите ваш скрипт, чтобы запросить глобальный
svgRoot
; если он не существует, используйте document.documentElement
.
- При извлечении SVG установите глобальный
svgRoot
для нового элемента svg
после его импорта в документ.
Вот код в действии:
И, если мой сайт не работает, вот код для потомков:
Скрипт-created.svg
<svg xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript"><![CDATA[
function createOn( root, name, a ){
var el = document.createElementNS(svgNS,name);
for (var n in a) if (a.hasOwnProperty(n)) el.setAttribute(n,a[n]);
return root.appendChild(el);
}
// Trust someone else for the root, in case we're being
// imported into another document
if (!window.svgRoot) svgRoot=document.documentElement;
var svgNS = svgRoot.namespaceURI;
createOn(svgRoot,'rect',{
x:10, y:10, width:30, height:30,
stroke:'#8080ff', "stroke-width":5,
fill:"none"
});
]]></script>
</svg>
import_svg_with_script.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="content-type"
content="application/xhtml+xml;charset=utf-8" />
<title>Fetch and Include Scripted SVG in XHTML</title>
<script type="text/ecmascript"><![CDATA[
setTimeout(function(){
var xhr = new XMLHttpRequest;
xhr.open('get','script-created.svg',true);
xhr.onreadystatechange = function(){
if (xhr.readyState != 4) return;
var svg = xhr.responseXML.documentElement;
svg = cloneToDoc(svg);
window.svgRoot = svg; // For reference by scripts
document.body.appendChild(svg);
delete window.svgRoot;
};
xhr.send();
},1000);
function cloneToDoc(node,doc){
if (!doc) doc=document;
var clone = doc.createElementNS(node.namespaceURI,node.nodeName);
for (var i=0,len=node.attributes.length;i<len;++i){
var a = node.attributes[i];
if (/^xmlns\b/.test(a.nodeName)) continue; // IE can't create these
clone.setAttributeNS(a.namespaceURI,a.nodeName,a.nodeValue);
}
for (var i=0,len=node.childNodes.length;i<len;++i){
var c = node.childNodes[i];
clone.insertBefore(
c.nodeType==1 ? cloneToDoc(c,doc) : doc.createTextNode(c.nodeValue),
null
)
}
return clone;
}
]]></script>
</head><body></body></html>