examples - El apéndice de jquery no funciona con svg element?
svg html5 generator (13)
Basado en la respuesta de @ chris-dolphin pero usando 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);
Asumiendo esto:
<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?
Cuando pasa una cadena de marcado a $
, 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
u otra forma (*) 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 una xmlns
dentro de un documento HTML ( 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 parte de un documento HTML.
Sin embargo, para los navegadores de hoy en día, debe usar X HTML (servido como application/xhtml+xml
; save con la extensión de archivo .xhtml para pruebas locales) para que SVG funcione. (De alguna manera tiene sentido, SVG es un estándar correctamente basado en XML.) Esto significa que tendría que 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, existe el parseWithContext de DOM Level 3 LS, pero el soporte 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 HTMLE utilizando innerHTML
y luego transferirlo al objetivo deseado. Sin embargo, será un poco más lento:
<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>
**: Odio la forma en que los autores de HTML5 parecen temer XML y están decididos a encadenar las funciones basadas en XML en el lío de HTML. XHTML resolvió estos problemas hace años.
Encontré una manera fácil que funciona con todos los navegadores que tengo (Chrome 49, Edge 25, Firefox 44, IE11, 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>
Esto funciona para mí hoy con FF 57:
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");
}
Hace:
JQuery no puede agregar elementos a <svg>
(parece agregarlos en el explorador DOM, pero no en la pantalla).
Una solución alternativa es agregar un <svg>
con todos los elementos que necesita a la página y luego modificar los atributos de los elementos con .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
});
});
La biblioteca D3 cada vez más popular maneja las rarezas de agregar / manipular svg 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");
La respuesta aceptada muestra una forma demasiado complicada. Como dice Forresto en su respuesta , " parece agregarlos en el explorador de DOM, pero no en la pantalla " y la razón de esto son diferentes espacios de nombres para html y svg.
La solución más fácil es "actualizar" toda la svg. Después de agregar el círculo (u otros elementos), usa esto:
$("body").html($("body").html());
Esto hace el truco. El círculo está en la pantalla.
O si lo desea, use un contenedor div:
$("#cont").html($("#cont").html());
Y envuelva 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:
- puedes editar svg existente (que ya está en DOM), 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.
EDITAR:
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) la vinculación entre los objetos de Rafael y SVG DOM se rompe si $("#cont").html($("#cont").html());
es usado. La solución a esto no es usar $("#cont").html($("#cont").html());
en absoluto y en lugar de usar el documento ficticio SVG.
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 archivo ficticio 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 todo el documento SVG, el navegador maneja automáticamente la conversión del espacio de nombres de todos los elementos en el documento SVG.
Un ejemplo ilumine 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)");
La demostración completa de esta técnica está aquí: http://jsbin.com/ilinan/1/edit .
(Todavía no tengo idea de por qué $("#cont").html($("#cont").html());
no funciona cuando se usa Raphael. Sería muy corto.
La respuesta aceptada por Bobince es una solución corta y portátil. Si necesita no solo agregar SVG sino también manipularlo, puede probar la biblioteca de JavaScript "Pablo" (lo escribí). Se sentirá familiar para los usuarios de jQuery.
Su ejemplo de código se vería así:
$(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 el marcado:
var circle = Pablo.circle({
cx:100,
cy:50,
r:40
}).appendTo(''svg'');
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 nombres correcto y luego jQuery-ify desde allí. Al igual que:
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 debe crear cada elemento SVG con el espacio de nombres correcto individualmente o no funcionará.
Puedo ver el círculo en firefox, haciendo 2 cosas:
1) Cambiar el nombre del archivo de html a xhtml
2) Cambiar guión a
<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>
Si la cadena que necesita adjuntar es SVG y agrega el espacio de nombre adecuado, puede analizar la cadena como XML y anexar a la matriz.
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))
Una forma mucho más simple es simplemente generar su SVG en una cadena, crear un elemento HTML contenedor e insertar la cadena svg en el elemento HTML usando $("#wrapperElement").html(svgString)
. Esto funciona bien en Chrome y Firefox.
var svg; // if you have variable declared and not assigned value.
// then you make a mistake by appending elements to that before creating element
svg.appendChild(document.createElement("g"));
// at some point you assign to svg
svg = document.createElementNS(''http://www.w3.org/2000/svg'', "svg")
// then you put it in DOM
document.getElementById("myDiv").appendChild(svg)
// it wont render unless you manually change myDiv DOM with DevTools
// to fix assign before you append
var svg = createElement("svg", [
["version", "1.2"],
["xmlns:xlink", "http://www.w3.org/1999/xlink"],
["aria-labelledby", "title"],
["role", "img"],
["class", "graph"]
]);
function createElement(tag, attributeArr) {
// .createElementNS NS is must! Does not draw without
let elem = document.createElementNS(''http://www.w3.org/2000/svg'', tag);
attributeArr.forEach(element => elem.setAttribute(element[0], element[1]));
return elem;
}
// extra: <circle> for example requires attributes to render. Check if missing.