Как динамически вставить изображение SVG в HTML?
У меня есть код, который получает сценарий SVG-изображение с сервера через Ajax. Я могу вернуть текст изображения обратно в браузер, но не могу найти способ вставить его в DOM, который будет отображать его. Может кто-нибудь помочь с этим? SVG выглядит так:
<svg id="chart" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
<script type="application/ecmascript">
<![CDATA[
...lots of code, changes on each Ajax request
//]]>
</script>
<script type="application/ecmascript" xlink:href="js-on-server-1.js"/>
<script type="application/ecmascript" xlink:href="js-on-server-2.js"/>
</svg>
Я пробовал разные вещи. Если я сделаю это:
// xmlhttp.onreadystatechange:
addImage(xmlhttp.responseXML, "somewhere");
...
function addImage(txt, dst_id) {
var scr = document.createElement("div");
if("textContent" in scr)
scr.textContent = txt; // everybody else
else
scr.text = txt; // IE
document.getElementById(dst_id).appendChild(scr);
}
Тогда Opera и Chrome ничего не делают, а F/F жалуется на "[объект XMLDocument]". Если я изменю 'responseXML' на 'responseText', то Opera/Chrome правильно отображает весь текст SVG (не изображение) в нужном месте, и F/F по-прежнему выдает то же предупреждение. Я также попытался назначить ответ на innerHTML, но это ничего не делает. Есть идеи? Благодарю.
РЕДАКТИРОВАТЬ
В ответ на ответ Phrogz'z ниже - я добавил два простых файла SVG. Первым является "стандартный" простой SVG, отображающий круг. Второй сценарий svg, отображающий прямоугольник. Вы должны иметь возможность просматривать оба непосредственно в любом браузере, кроме IE8-. Если я отредактирую код Phrogz'z для использования файла окружности (замените 'stirling4.svg' на имя этого файла), то это сработает, но если я хочу вместо этого прямоугольник со сценарием, то нет. Проверено на F/F, Opera, Chromium, но все равно не работает на (моем) Chromium.
Файл 1, круг:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
</svg>
Файл 2, прямоугольник:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
<script type="application/ecmascript">
<![CDATA[
var svgDocument;
var svgns = "http://www.w3.org/2000/svg";
function init(evt) {
if(window.svgDocument == null)
svgDocument = evt.target.ownerDocument;
var lbox = svgDocument.createElementNS(svgns, "rect");
lbox.setAttributeNS(null, "x", 10);
lbox.setAttributeNS(null, "y", 10);
lbox.setAttributeNS(null, "width", 30);
lbox.setAttributeNS(null, "height", 30);
lbox.setAttributeNS(null, "stroke", "#8080ff");
lbox.setAttributeNS(null, "stroke-width", 2);
lbox.setAttributeNS(null, "fill-opacity", 0);
lbox.setAttributeNS(null, "stroke-opacity", 1);
lbox.setAttributeNS(null, "stroke-dasharray", 0);
svgDocument.documentElement.appendChild(lbox);
}
//]]>
</script>
</svg>
Предположительно, ответ заключается в том, чтобы получить скрипт в шапку??
1 ответ
В общем, проблема двоякая:
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
элемент после импорта его в документ.
Вот код в действии:
- Автономный скрипт SVG: http://phrogz.net/SVG/script-created.svg
- Страница, удобная для IE9, которая ее импортирует: http://phrogz.net/SVG/import_svg_with_script.xhtml
И, если мой сайт не работает, вот код для потомков:
скрипт-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>