Есть ли какая-то замена innerHTML в SVG / XML? - PullRequest
14 голосов
/ 15 марта 2012

В 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 ]

11 голосов
/ 15 марта 2012

Вы можете использовать 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));
7 голосов
/ 22 марта 2012

Ознакомьтесь с innerSVG javascript shim, он обеспечивает необходимую вам функциональность.

2014 обновление: Спецификация разбора DOM определяет innerHTML и outerHTML для Element, что делает их доступными для элементов svg и xml. Это уже давно в Blink, первые версии для поддержки этого были Chrome 32 / Opera 19, более подробную информацию можно найти в этом отчете об ошибках .

4 голосов
/ 15 февраля 2013

Здесь я пишу грязным способом ...

обходное решение innerHTML для 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>

Наслаждайтесь!

4 голосов
/ 09 мая 2012

Как насчет моей прокладки 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
3 голосов
/ 16 марта 2012

Краткий ответ: «Нет, в мире 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
1 голос
/ 29 мая 2015

Проще говоря, document.querySelector('#myContainer').textContent = newContent; имеет довольно хорошую поддержку, def. на IE9 +, Safari, FF, Chrome.

Майк Босток - мой герой в таких вещах: исходный код D3.js - мой способ ответить на вопросы SVG.

1 голос
/ 13 января 2013

Похоже, по крайней мере, в 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>
1 голос
/ 15 марта 2012

С помощью jQuery вы можете сделать это следующим образом:

Предположим, что ваш svgString содержит ваше изображение svg после операций замены.

$(svgString)[0], чтобы создать тег svg, соответствующийтвоя строкаЗатем вы можете добавить этот элемент в том месте, где хотите нарисовать изображение.

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...