Is there some innerHTML replacement in SVG/XML?

31,248

Solution 1

You can use DOMParser to parse XML. You can then use importNode to get that into your existing document if you want via importNode to end up with something like this...

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));

Solution 2

Check out the innerSVG javascript shim, it provides the functionality you want.

2014 update: The DOM parsing spec defines innerHTML and outerHTML on Element, which makes these available on svg and xml elements. This has been shipping in Blink for a while now, first versions to support this was Chrome 32 / Opera 19, more details can be found in this bugreport.

Solution 3

Here I write a dirty way...

innerHTML workaround for 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>

Enjoy!

Solution 4

The short answer is "No, there is nothing equivalent in the world of XML that lets you hand it a bit of markup and have it automatically create all the elements and attributes in the proper namespaces for the location where you insert it."

The closest direct answer is what @Robert has. As noted in my comments, even then you'll need to create any snippets inside an SVG document that has the same namespaces and prefixes as the document into which you'll be inserting the fragment.

Instead, you might find it is as easy (or easier) to use a convenience method on the standard DOM methods:

// 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);
}

With this you can do things like:

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);
}

This is IMHO cleaner than trying to construct the raw SVG markup using string concatenation:

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

Solution 5

With jQuery, you can do it this way:

Let's suppose your svgString contains your svg image after the replacing operations.

$(svgString)[0] to create a svg tag corresponding to your string. Then you can append this element where you want in the dom to draw the image.

I hope this helps

Share:
31,248
Sirko
Author by

Sirko

Researcher @ German Aerospace Center (DLR) Insitute of Data Science Group for Data Management and Analysis

Updated on July 09, 2022

Comments

  • Sirko
    Sirko almost 2 years

    In HTML I can build a simple templating system by providing a template in form of a string, replace some parts of it and then assign it using innerHTML to some container.

    var templ = '<span>{myText}</span>'
    var newContent = templ.replace( '{myText}', someVariable );
    document.querySelector( '#myContainer' ).innerHTML = newContent;
    

    This way I can take advantage of the browser's HTML parser and do not have to repeatedly use document.createElement(). The later can be quite cumbersome, if the templates grows beyond a few elements.

    In SVG, however, there is no property on the elements as innerHTML or even innerSVG for that matter.

    So my question is: Is there anything I can use in SVG ro resemble the approach from the example above or am I stuck with document.createElement() (or respectivly some lib that uses it)?

    As always with my questions: Vanilla JavaScript solutions are preferred, but any pointer to a lib providing a solution is appreciated.

  • Phrogz
    Phrogz over 12 years
    Are you certain that this will create elements in the proper namespace?
  • Phrogz
    Phrogz over 12 years
    This requires the fragment to be a valid XML document (single root only), right? And for fragments not parented by an SVG element with the proper namespace, would the created elements have a correct namespace?
  • Phrogz
    Phrogz over 12 years
    You may want/need to wrap this up as a function that wraps the string in an SVG root with all common namespaces and prefixes predefined, and then extract the contents.
  • Robert Longson
    Robert Longson over 12 years
    Yes, the fragment would need a namespace attribute.
  • Sirko
    Sirko about 9 years
    innerHTML has some functionality beyond just setting the text-value of a node, but triggers HTML parsing. So I think I'm not able to add new nodes by using textContent.
  • AJFarkas
    AJFarkas about 9 years
    Good catch. Yeah, I usually use document.createElement(...), but could write a function that handles it.
  • nroose
    nroose about 9 years
    Hey, do you know if there is a way to get innerSVG to work with SVG that is in an <object>? Seems like it works only for inline svg, and our stuff currently only works with SVG in an <object>... Thanks.
  • Erik Dahlström
    Erik Dahlström about 9 years
    @nroose yes, it's possible, see xn--dahlstrm-t4a.net/svg/html/…. If you can't get it to work please post a new question.
  • Bharata
    Bharata over 5 years
    It does not work! I have tested it with IE10 (it is from year 2012) and.current Opera version. Did you ever test it?