Есть ли какая-то замена innerHTML в SVG/XML?
В HTML я могу создать простую систему шаблонов, предоставляя шаблон в виде строки, заменяя некоторые его части, а затем назначая его с помощью innerHTML
в какой-то контейнер.
var templ = '<span>{myText}</span>'
var newContent = templ.replace( '{myText}', someVariable );
document.querySelector( '#myContainer' ).innerHTML = newContent;
Таким образом, я могу воспользоваться HTML-парсером браузера и не использовать повторно document.createElement()
, Последнее может быть довольно громоздким, если шаблоны выходят за рамки нескольких элементов.
В SVG, однако, отсутствует свойство элементов, так как innerHTML
или даже innerSVG
в этом отношении.
Итак, мой вопрос: есть ли что-нибудь, что я могу использовать в SVG ro, напоминает подход из приведенного выше примера, или я застрял с document.createElement()
(или соответственно какая-то библиотека, которая его использует)?
Как всегда с моими вопросами: предпочтительны решения Vanilla JavaScript, но приветствуется любой указатель на библиотеку, предоставляющую решение.
8 ответов
Вы можете использовать DOMParser для анализа XML: https://developer.mozilla.org/en/Parsing_and_serializing_XML, затем вы можете использовать importNode, чтобы добавить его в существующий документ, если хотите: https://developer.mozilla.org/en/DOM/document.importNode чтобы закончить с чем-то вроде этого...
var doc = new DOMParser().parseFromString(
'<svg xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="100" r="20"/></svg>',
'application/xml');
someElement.appendChild(
someElement.ownerDocument.importNode(doc.documentElement, true));
Обратите внимание на внутреннюю часть javascript shim innerSVG, она обеспечивает необходимую вам функциональность.
2014 обновление: спецификация синтаксического анализа DOM innerHTML
а также outerHTML
на Element
, что делает их доступными для элементов svg и xml. Это было в продаже в Blink некоторое время, первые версии для поддержки этого были Chrome 32 / Opera 19, более подробную информацию можно найти в этом отчете об ошибках.
Здесь я пишу грязный способ...
Внутренний обходной путь для SVG
http://jsfiddle.net/microbians/8ztNU/
<html>
<body>
<svg id="svgcanvas">
</svg>
<script>
var twocircles='<circle cx="253" cy="562" r="10" stroke="black" stroke-width="2" fill="red"></circle> \
<circle cx="353" cy="562" r="10" stroke="black" stroke-width="2" fill="red"></circle>'
var receptacle = document.createElement('div');
var svgfragment='<svg>'+twocircles+'</svg>';
receptacle.innerHTML=''+svgfragment;
var Nodes=Array.prototype.slice.call(receptacle.childNodes[0].childNodes);
Nodes.forEach(function(el){document.getElementById('svgcanvas').appendChild(el)})
</script>
</body>
</html>
Наслаждайтесь!
Как насчет моей прокладки innerSVG? Ниже приведен исходный код CoffeeScript, скомпилированный JavaScript находится по https://gist.github.com/2648095.
# Important: You must serve your pages as XHTML for this shim to work,
# otherwise namespaced attributes and elements will get messed up.
Object.defineProperty SVGElement.prototype, 'innerHTML',
get: () ->
$temp = document.createElement 'div'
$node = @cloneNode true
for $child in $node.children
$temp.appendChild $child
return $temp.innerHTML
set: (markup) ->
while @firstChild
@firstChild.parentNode.removeChild @firstChild
markup = "<svg id='wrapper' xmlns='http://www.w3.org/2000/svg'>#{markup}</svg>"
$div = document.createElement 'div'
$div.innerHTML = markup
$svg = $div.querySelector 'svg#wrapper'
for $element in $svg.children
@appendChild $element
enumerable : false
configurable : true
Короткий ответ: "Нет, нет ничего эквивалентного в мире XML, который позволил бы вам передать немного разметки и автоматически создать все элементы и атрибуты в надлежащих пространствах имен для места, где вы его вставляете".
Самый близкий прямой ответ - то, что имеет @Robert. Как отмечалось в моих комментариях, даже в этом случае вам потребуется создавать любые фрагменты внутри документа SVG, которые имеют те же пространства имен и префиксы, что и документ, в который вы будете вставлять фрагмент.
Вместо этого вы можете обнаружить, что проще (или проще) использовать удобный метод для стандартных методов DOM:
// Create a named SVG element on a node, with attributes and optional text
function appendTo(node,name,attrs,text){
var p,ns=appendTo.ns,svg=node,doc=node.ownerDocument;
if (!ns){ // cache namespaces by prefix once
while (svg&&svg.tagName!='svg') svg=svg.parentNode;
ns=appendTo.ns={svg:svg.namespaceURI};
for (var a=svg.attributes,i=a.length;i--;){
if (a[i].namespaceURI) ns[a[i].localName]=a[i].nodeValue;
}
}
var el = doc.createElementNS(ns.svg,name);
for (var attr in attrs){
if (!attrs.hasOwnProperty(attr)) continue;
if (!(p=attr.split(':'))[1]) el.setAttribute(attr,attrs[attr]);
else el.setAttributeNS(ns[p[0]]||null,p[1],attrs[attr]);
}
if (text) el.appendChild(doc.createTextNode(text));
return node.appendChild(el);
}
function clear(node){
while (node.lastChild) node.removeChild(node.lastChild);
}
С этим вы можете делать такие вещи, как:
var icons={
Apps : "/images/apps.png",
Games : "/images/games.png"
}
var wrap = document.querySelector('#container');
clear(wrap);
for (var label in icons){
if (!icons.hasOwnProperty(label)) continue;
var icon = appendTo(wrap,'g',{'class':'icon'});
appendTo(icon,'image',{'xlink:href':icons[label]});
appendTo(icon,'text',{x:10,y:20},label);
}
Это IMHO чище, чем пытаться создать необработанную разметку SVG с использованием конкатенации строк:
var svg = [];
for (var label in icons){
if (!icons.hasOwnProperty(label)) continue;
svg.push('<g class="icon">');
svg.push('<image xlink:href="'+icons[label]+'" />');
svg.push('<text x="10" y="20">'+label+'</text>');
svg.push('</g>');
}
wrap.innerSVG = svg.join(''); // doesn't work, of course
С помощью jQuery вы можете сделать это следующим образом:
Давайте предположим, что ваш svgString
содержит ваше изображение SVG после операций замены.
$(svgString)[0]
создать тег svg, соответствующий вашей строке. Затем вы можете добавить этот элемент, где вы хотите в DOM рисовать изображение.
надеюсь, это поможет
Похоже, по крайней мере, в Chrome и Safari, вы можете заключить свой элемент SVG в элемент HTML и запросить innerHTML
, Следующий HTML-файл отображает красный прямоугольник, хотя источник SVG указывает зеленый цвет.
<body>
<div id="wrapper">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="120" height="120" viewBox="0 0 236 120">
<rect x="14" y="23" width="250" height="50" fill="green" stroke="black" stroke-width="1" />
</svg>
</div>
<script>
var el = document.getElementById('wrapper');
var t = el.innerHTML;
t = t.replace(/green/g, "red");
el.innerHTML = t;
</script>
</body>
Проще говоря, document.querySelector('#myContainer').textContent = newContent;
имеет довольно хорошую поддержку, def. на IE9+, Safari, FF, Chrome.
Майк Босток - мой герой в таких вещах: исходный код D3.js - это мой способ ответить на вопросы SVG.