2010-09-04 12 views
168

Suponiendo esto:jquery's append no funciona con svg element?

<html> 
<head> 
<script type="text/javascript" src="jquery.js"></script> 
<script type="text/javascript"> 
$(document).ready(function(){ 
    $("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'); 
}); 
</script> 
</head> 
<body> 
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px"> 
</svg> 
</body> 

¿Por qué no veo nada?

+0

Qué navegador? – Topera

Respuesta

211

Cuando se pasa una cadena de marcado en $, se analiza como HTML usando la propiedad innerHTML del navegador en un <div> (u otro contenedor adecuado para casos especiales como <tr>). innerHTML no puede analizar SVG u otro contenido que no sea HTML, e incluso si pudiera no podría decir que se suponía que <circle> estaba en el espacio de nombres SVG.

innerHTML no está disponible en SVGElement: solo es propiedad de HTMLElement. Tampoco existe actualmente una propiedad innerSVG o de otra manera (*) para analizar el contenido en un elemento SVGElement. Por esta razón, debe usar métodos de estilo DOM. jQuery no le da acceso fácil a los métodos de espacio de nombres necesarios para crear elementos SVG. En realidad, jQuery no está diseñado para usarse con SVG en absoluto y muchas operaciones pueden fallar.

HTML5 promete permitirte usar <svg> sin un xmlns dentro de un documento HTML simple (text/html) en el futuro. Pero esto es solo un truco analizador (**), el contenido SVG seguirá siendo elementos SVGE en el espacio de nombres SVG, y no HTMLElements, por lo que no podrás usar innerHTML aunque parezcan como parte de un documento HTML .

Sin embargo, para los navegadores de hoy en día debe utilizar X HTML (adecuadamente servido como application/xhtml+xml, guardar con la extensión de archivo .xhtml para la prueba local) para obtener SVG para trabajar en absoluto. (De alguna manera, tiene sentido SVG es un estándar correctamente basado en XML.) Esto significa que debe escapar de los símbolos < dentro de su secuencia de comandos (o encerrar en una sección CDATA) e incluir la declaración XHTML xmlns. ejemplo:

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"><head> 
</head><body> 
    <svg id="s" xmlns="http://www.w3.org/2000/svg"/> 
    <script type="text/javascript"> 
     function makeSVG(tag, attrs) { 
      var el= document.createElementNS('http://www.w3.org/2000/svg', tag); 
      for (var k in attrs) 
       el.setAttribute(k, attrs[k]); 
      return el; 
     } 

     var circle= makeSVG('circle', {cx: 100, cy: 50, r:40, stroke: 'black', 'stroke-width': 2, fill: 'red'}); 
     document.getElementById('s').appendChild(circle); 
     circle.onmousedown= function() { 
      alert('hello'); 
     }; 
    </script> 
</body></html> 

*: bueno, hay DOM Nivel 3 de LS parseWithContext, pero el apoyo del navegador es muy pobre. Editar para agregar: sin embargo, aunque no puede inyectar marcas en un elemento SVGE, puede inyectar un nuevo elemento SVGE en un elemento HTMLElement utilizando innerHTML, y luego transferirlo al objetivo deseado. Es probable que va a ser un poco más lento sin embargo:

<script type="text/javascript"><![CDATA[ 
    function parseSVG(s) { 
     var div= document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); 
     div.innerHTML= '<svg xmlns="http://www.w3.org/2000/svg">'+s+'</svg>'; 
     var frag= document.createDocumentFragment(); 
     while (div.firstChild.firstChild) 
      frag.appendChild(div.firstChild.firstChild); 
     return frag; 
    } 

    document.getElementById('s').appendChild(parseSVG(
     '<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" onmousedown="alert(\'hello\');"/>' 
    )); 
]]></script> 

**: No me gusta la forma en que los autores de HTML5 parecen tener miedo de XML y decidido a meter con calzador características basadas en XML en el lío enrevesada que es HTML. XHTML resolvió estos problemas hace años.

+0

Esto podría resolverse modificando jQuery si solo exponen 'wrapMap' ... – James

+4

¡Esta respuesta todavía es relevante! Acabo de tener un error extraño donde los elementos agregados se muestran en el inspector de elementos de Chrome, pero no se procesa. Si '' RMB'> 'edita como html' en la etiqueta html y presiono" Enter ", todo se muestra (pero todos los oyentes de eventos desaparecen). Después de leer esta respuesta, cambié mis llamadas de createElement a createElementNS y ahora todo funciona. –

+5

Puede manipular los elementos SVG con jquery una vez que se crean utilizando el método DOM createElementNS. Puede cambiar la función makeSVG a 'return $ (el)' y ahora tiene un elemento svg que puede usar métodos jquery. – Hoffmann

7

Veo círculo en Firefox, haciendo 2 cosas:

1 archivo de cambio de nombre) de HTML a XHTML

2) Cambio de secuencia de comandos para

<script type="text/javascript"> 
$(document).ready(function(){ 
    var obj = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 
    obj.setAttributeNS(null, "cx", 100); 
    obj.setAttributeNS(null, "cy", 50); 
    obj.setAttributeNS(null, "r", 40); 
    obj.setAttributeNS(null, "stroke", "black"); 
    obj.setAttributeNS(null, "stroke-width", 2); 
    obj.setAttributeNS(null, "fill", "red"); 
    $("svg")[0].appendChild(obj); 
}); 
</script> 
23

JQuery no puede agregar elementos a <svg> (parece agregarlos en el explorador DOM, pero no en la pantalla).

Una solución es agregar un <svg> con todos los elementos que necesita a la página, y luego modificar los atributos de los elementos usando .attr().

$('body') 
    .append($('<svg><circle id="c" cx="10" cy="10" r="10" fill="green" /></svg>')) 
    .mousemove(function (e) { 
     $("#c").attr({ 
      cx: e.pageX, 
      cy: e.pageY 
     }); 
    }); 

http://jsfiddle.net/8FBjb/1/

+0

¡Gracias, me ayudaron mucho! Este es un muy buen enfoque. En lugar de anexar '' u otros elementos de forma individual utilizando métodos DOM complejos y lentos (por ejemplo, 'createDocumentFragment()' o 'createElementNS()'), añada todo el documento svg al contenedor. En lugar de $ ('cuerpo') también puede ser $ ('div'). Y después de eso, el documento svg está en DOM y se puede consultar de forma familiar utilizando, por ejemplo. $ ('c'). attr ('fill', 'red'). –

+0

Hice un ejemplo adicional de esto: http://jsbin.com/inevaz/1/. Obtiene el código fuente de tiger.svg de internet en iframe y se puede copiar y pegar en textarea. El contenido de textarea se usa como fuente para svg en línea, que luego se puede acceder a través de DOM (para cambiar el estilo de trazo). –

+0

Esto es genial, no sé por qué todos votan por esas otras respuestas que simplemente no funcionan. –

116

El accepted answer muestra demasiado complicado. Como afirma Forresto en his answer, "parece agregarlos en el explorador de DOM, pero no en la pantalla" y el motivo de esto son diferentes espacios de nombres para html y svg.

La solución más fácil es "actualizar" toda svg. Después de agregar el círculo (u otros elementos), utilice esto:

$("body").html($("body").html()); 

Esto lo hace el truco. El círculo está en la pantalla.

O si lo desea, utilice un contenedor div:

$("#cont").html($("#cont").html()); 

Y envolver su SVG dentro de contenedor div:

<div id="cont"> 
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px"> 
    </svg> 
</div> 

El ejemplo funcional:
http://jsbin.com/ejifab/1/edit

Las ventajas de esta técnica:

  • puede editar svg existente (que ya está en DOM), por ej. creado usando Raphael o como en su ejemplo "codificado" sin scripting.
  • puede agregar estructuras de elementos complejos como cadenas, por ejemplo. $('svg').prepend('<defs><marker></marker><mask></mask></defs>'); como lo haces en jQuery.
  • después de que los elementos se anexen y se hagan visibles en la pantalla usando $("#cont").html($("#cont").html()); sus atributos se pueden editar usando jQuery.

EDIT:

La técnica anterior funciona con "codificado" o DOM manipulado (= document.createElementNS etc.) SVG solamente. Si Raphael se usa para crear elementos, (de acuerdo con mis pruebas) el enlace entre los objetos de Rafael y SVG DOM se rompe si se usa $("#cont").html($("#cont").html());. La solución a esto no es utilizar $("#cont").html($("#cont").html()); en absoluto y en lugar de usar el documento SVG ficticio.

Este SVG ficticio es primero una representación textual del documento SVG y contiene solo los elementos que se necesitan. Si queremos, por ej. para agregar un elemento de filtro al documento de Raphael, el maniquí podría ser algo así como <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. La representación textual se convierte primero a DOM utilizando el método jQuery's $ ("body"). Append(). Y cuando el elemento (filtro) está en DOM, se puede consultar utilizando los métodos jQuery estándar y anexar al documento SVG principal creado por Raphael.

¿Por qué se necesita este maniquí? ¿Por qué no agregar un elemento de filtro estrictamente al documento creado por Raphael? Si lo intentas usando, por ej. $("svg").append("<circle ... />"), se crea como elemento html y no hay nada en la pantalla como se describe en las respuestas. Pero si se adjunta el documento SVG completo, el navegador maneja automáticamente la conversión del espacio de nombres de todos los elementos en el documento SVG.

Un ejemplo iluminar la técnica:

// Add Raphael SVG document to container element 
var p = Raphael("cont", 200, 200); 
// Add id for easy access 
$(p.canvas).attr("id","p"); 
// Textual representation of element(s) to be added 
var f = '<filter id="myfilter"><!-- filter definitions --></filter>'; 

// Create dummy svg with filter definition 
$("body").append('<svg id="dummy" style="display:none"><defs>' + f + '</defs></svg>'); 
// Append filter definition to Raphael created svg 
$("#p defs").append($("#dummy filter")); 
// Remove dummy 
$("#dummy").remove(); 

// Now we can create Raphael objects and add filters to them: 
var r = p.rect(10,10,100,100); 
$(r.node).attr("filter","url(#myfilter)"); 

demostración de trabajo completa de esta técnica es aquí: http://jsbin.com/ilinan/1/edit.

(I tiene (todavía) ni idea, ¿por qué $("#cont").html($("#cont").html()); no funciona cuando se utiliza Raphael. Sería muy corto truco.)

+0

Gracias, tu manera de acercarte también funcionó. –

+0

Estoy usando mi solución (hecha en casa) para manejar SVG y tengo el mismo problema que Raphael, cuando uso el "truco de reparación" con $ (# imagen) .html ..., * pero * mi solución era volver a iniciar algunos elementos SVG almacenados en caché (marcado de rectángulo, transformación, etc.) – halfbit

+0

Eres un mago. Estaba viendo la respuesta aceptada y pensé que esto sería un dolor. Luego, vi esto y hace todo lo que necesito en una línea corta. ¡Gracias! –

31

El cada vez más popular D3 biblioteca se encarga de las singularidades de la anadiendo/SVG manipular muy bien . Es posible que desee considerar usarlo en lugar de los hack jQuery mencionados aquí.

HTML

<svg xmlns="http://www.w3.org/2000/svg"></svg> 

Javascript

var circle = d3.select("svg").append("circle") 
    .attr("r", "10") 
    .attr("style", "fill:white;stroke:black;stroke-width:5"); 
+0

buena respuesta. Me ayudó con algún problema aquí. –

+0

jQuery también se ahoga en configurar/obtener clases de SVG también. Solo un lado. – QueueHammer

3

La respuesta aceptada por bobince es una solución a corto y portátil. Si necesita no solo agregar SVG sino también manipularlo, puede probar el JavaScript library "Pablo" (lo escribí). Se sentirá familiar para los usuarios de jQuery.

Su ejemplo de código sería el siguiente aspecto:

$(document).ready(function(){ 
    Pablo("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'); 
}); 

También puede crear elementos SVG sobre la marcha, sin especificar marcado:

var circle = Pablo.circle({ 
    cx:100, 
    cy:50, 
    r:40 
}).appendTo('svg'); 
9

No he visto a alguien mencionar este método, pero document.createElementNS() es útil en esta instancia.

Puede crear los elementos usando Javascript vainilla como nodos DOM normales con el espacio de nombre correcto y luego jQuery-ify desde allí. De este modo:

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), 
    circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 

var $circle = $(circle).attr({ //All your attributes }); 

$(svg).append($circle); 

El único inconveniente es que usted tiene que crear cada elemento SVG con el espacio de nombres derecho individual o no va a funcionar.

-1

Una forma mucho más simple es simplemente generar su SVG en una cadena, crear un elemento HTML de envoltura e insertar la cadena svg en el elemento HTML usando $("#wrapperElement").html(svgString). Esto funciona bien en Chrome y Firefox.

+1

no funciona para mí, intenté este par de veces – Bhagya

4

Sobre la base de @ respuesta Chris-delfín 's pero utilizando la función auxiliar:

// Creates svg element, returned as jQuery object 
function $s(elem) { 
    return $(document.createElementNS('http://www.w3.org/2000/svg', elem)); 
} 

var $svg = $s("svg"); 
var $circle = $s("circle").attr({...}); 
$svg.append($circle); 
+1

Por supuesto también puede : '$ svg.append ($ s ('')) ; ' –

4

encontrado una manera fácil que funciona con todos los navegadores que tengo (Chrome 49, Edge 25, Firefox 44, EI11, Safari 5 [Win], Safari 8 (MacOS)):

// Clean svg content (if you want to update the svg's objects) 
 
// Note : .html('') doesn't works for svg in some browsers 
 
$('#svgObject').empty(); 
 
// add some objects 
 
$('#svgObject').append('<polygon class="svgStyle" points="10,10 50,10 50,50 10,50 10,10" />'); 
 
$('#svgObject').append('<circle class="svgStyle" cx="100" cy="30" r="25"/>'); 
 

 
// refresh DOM (you must refresh svg's parent for Edge/IE and Safari) 
 
$('#svgContainer').html($('#svgContainer').html());
.svgStyle 
 
{ 
 
    fill:cornflowerblue; 
 
    fill-opacity:0.2; 
 
    stroke-width:2; 
 
    stroke-dasharray:5,5; 
 
    stroke:black; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 

 
<div id="svgContainer"> 
 
    <svg id="svgObject" height="100" width="200"></svg> 
 
</div> 
 

 
<span>It works if two shapes (one square and one circle) are displayed above.</span>

+2

Esa línea de actualización es exactamente lo que estaba buscando. Navegadores tontos. ¡Gracias! –

4

Si la cadena que es necesario añadir es SVG y se agrega el espacio de nombres adecuado, ca n analizar la cadena como XML y anexar al padre.

var xml = jQuery.parseXML('<circle xmlns="http://www.w3.org/2000/svg" cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'); 
$("svg").append(xml.documentElement)) 
-1

Esto es trabajo para mí hoy en día con 57 FF:

function() { 
    // JQuery, today, doesn't play well with adding SVG elements - tricks required 
    $(selector_to_node_in_svg_doc).parent().prepend($(this).clone().text("Your")); 
    $(selector_to_node_in_svg_doc).text("New").attr("x", "340").text("New") 
     .attr('stroke', 'blue').attr("style", "text-decoration: line-through"); 
} 

las marcas:

this SVG image as seen in Firefox 57

+0

No son elementos SVG los que está agregando, eso es contenido de texto. –

+0

Eso puede ser, pero se procesó ** después de ** la carga inicial de la página de SVG con líneas estáticas. –

+0

Así que, no es la pregunta que se hace, que es sobre los elementos svg y no el contenido de texto. –