2012-03-15 7 views
14

En HTML puedo construir un sistema de plantillas sencilla, proporcionando una plantilla en forma de una cadena, reemplazar algunas partes de ella y luego asignar usando innerHTML a algún recipiente.¿Hay algún reemplazo innerHTML en SVG/XML?

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

De esta manera puedo aprovechar analizador HTML del navegador y no tener que utilizar repetidamente document.createElement(). Lo posterior puede ser bastante engorroso, si las plantillas crecen más allá de unos pocos elementos.

En SVG, sin embargo, no hay ninguna propiedad en los elementos como innerHTML o incluso innerSVG para el caso.

Así que mi pregunta es: ¿Hay algo que pueda usar en SVG ro se parezca al enfoque del ejemplo anterior o me quedo con document.createElement() (o respetivamente alguna lib que lo use)?

Como siempre con mis preguntas: Se prefieren las soluciones de JavaScript Vanilla, pero se aprecia cualquier puntero a una lib que proporcione una solución.

Respuesta

10

Puede utilizar DOMParser a analizar XML: https://developer.mozilla.org/en/Parsing_and_serializing_XML a continuación, puede utilizar importNode para conseguir que en el documento existente si desea: https://developer.mozilla.org/en/DOM/document.importNode a terminar con algo como esto ...

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

Esto requiere que el fragmento sea un documento XML válido (solo raíz única), ¿verdad? Y para los fragmentos no emparentados por un elemento SVG con el espacio de nombre adecuado, ¿los elementos creados tienen un espacio de nombre correcto? – Phrogz

+0

Es posible que desee/necesite concluir esto como una función que envuelve la cadena en una raíz SVG con todos los espacios de nombres comunes y los prefijos predefinidos, y luego extraiga el contenido. – Phrogz

+0

Sí, el fragmento necesitaría un atributo de espacio de nombres. –

1

con jQuery, puede hacerlo de esta manera:

Supongamos que su svgString contiene su imagen SVG después de las operaciones que sustituyen.

$(svgString)[0] para crear una etiqueta SVG correspondiente a la cadena. Luego puedes agregar este elemento donde quieras en el dom para dibujar la imagen.

espero que esto ayude a

+0

¿Está seguro de que esto creará elementos en el espacio de nombres adecuado? – Phrogz

3

La respuesta breve es "No, no hay nada equivalente en el mundo de XML que le permita darle un poco de margen y crear automáticamente todos los elementos y atributos en los espacios de nombres adecuados para la ubicación donde inserta eso."

La respuesta directa más cercana es la que tiene @ Robert. Como se ha señalado en mis comentarios, incluso entonces tendrá que crear ninguna fragmentos dentro de un documento SVG que tiene los mismos espacios de nombres y prefijos como el documento en el que podrás insertar el fragmento.

su lugar, puede encontrar que es tan fácil (o más fácil) para utilizar un método de conveniencia de los métodos DOM estándar:

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

Con esto se puede hacer cosas como:

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

esta es mi humilde opinión más limpio que tratar de construir el marcado SVG cruda usando concatenación de cadenas:

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 
4

C sorprende el innerSVG javascript shim, proporciona la funcionalidad que desea.

2014 actualización: El DOM parsing spec define innerHTML y outerHTML en Element, lo que hace que estos posibles para los elementos SVG y XML. Este ha sido enviado en Blink por un tiempo, las primeras versiones para soportar esto fueron Chrome 32/Opera 19, más detalles se pueden encontrar en this bugreport.

+0

Oye, ¿sabes si hay una forma de hacer que innerSVG funcione con SVG que está en un ? Parece que funciona solo para svg en línea, y nuestras cosas actualmente solo funciona con SVG en un ... Gracias. – nroose

+0

@nroose sí, es posible, consulte http://xn--dahlstrm-t4a.net/svg/html/get-embedded-svg-document-script.html. Si no puede hacer que funcione, publique una nueva pregunta. –

4

¿Qué tal mi innerSVG shim? CoffeeScript fuente está por debajo, compilado JavaScript está en 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 
1

Parece que, al menos en Chrome y Safari, se puede envolver el elemento SVG en un elemento HTML y pedir innerHTML. El siguiente archivo HTML representa un rectángulo rojo, aunque la fuente SVG especifica verde.

<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> 
4

Aquí escribo una manera sucia ...

innerHtml solución para 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> 

disfrutar!

0

Más simplemente, document.querySelector('#myContainer').textContent = newContent; tiene bastante buen soporte, def. a IE9 +, Safari, FF, Chrome.

Mike Bostock es mi héroe para este tipo de cosas: el D3.js source code es mi pregunta para SVG.

+0

'innerHTML' tiene alguna funcionalidad más allá de simplemente establecer el valor de texto de un nodo, pero desencadena el análisis de HTML. Así que creo que no puedo agregar nuevos nodos usando 'textContent'. – Sirko

+0

Buena captura. Sí, normalmente uso 'document.createElement (...)', pero podría escribir una función que lo maneje. – AJFarkas