221 votes

L'append de jquery ne fonctionne pas avec l'élément svg ?

En supposant cela :

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

Pourquoi je ne vois rien ?

269voto

bobince Points 270740

Lorsque vous passez une chaîne de balisage dans $ il est analysé en tant que HTML à l'aide de la fonction innerHTML sur une <div> (ou un autre conteneur approprié pour les cas particuliers comme <tr> ). innerHTML ne peut pas analyser SVG ou tout autre contenu non-HTML, et même s'il le pouvait, il ne serait pas capable de dire que <circle> était censé être dans l'espace de noms SVG.

innerHTML n'est pas disponible sur SVGElement - c'est une propriété de HTMLElement seulement. Il n'existe pas non plus actuellement de innerSVG ou un autre moyen(*) d'analyser le contenu dans un SVGElement. Pour cette raison, vous devriez utiliser des méthodes de style DOM. jQuery ne vous donne pas un accès facile aux méthodes namespaced nécessaires pour créer des éléments SVG. En fait, jQuery n'est pas du tout conçu pour être utilisé avec SVG et de nombreuses opérations peuvent échouer.

HTML5 promet de vous permettre d'utiliser <svg> sans un xmlns dans un fichier HTML simple ( text/html ) à l'avenir. Mais ceci n'est qu'un hack de l'analyseur (**), le contenu SVG sera toujours des SVGElements dans l'espace de nom SVG, et non des HTMLElements, vous ne pourrez donc pas utiliser innerHTML même s'ils regardez comme une partie d'un document HTML.

Cependant, pour les navigateurs actuels, vous devez utiliser *X*HTML (correctement servi en tant que application/xhtml+xml (enregistrer avec l'extension de fichier .xhtml pour les tests locaux) pour que SVG fonctionne. (Il est logique de le faire de toute façon ; SVG est un standard basé sur XML.) Cela signifie que vous devez échapper l'extension < à l'intérieur de votre bloc script (ou dans une section CDATA), et inclure les symboles XHTML xmlns déclaration. exemple :

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

* : bien, il y a les DOM Niveau 3 LS's parseWithContext mais le support des navigateurs est très faible. Modifier pour ajouter : cependant, alors que vous ne pouvez pas injecter de balises dans un SVGElement, vous pouvez injecter un nouveau SVGElement dans un HTMLElement en utilisant innerHTML puis le transférer vers la cible souhaitée. Ce sera probablement un peu plus lent cependant :

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

** : Je déteste la façon dont les auteurs de HTML5 semblent avoir peur de XML et sont déterminés à insérer des fonctionnalités basées sur XML dans le fouillis de HTML. XHTML a résolu ces problèmes il y a des années.

8 votes

Cette réponse est toujours d'actualité ! Je viens de rencontrer un bogue bizarre : les éléments ajoutés s'affichent dans l'inspecteur d'éléments de Chrome, mais ne s'affichent pas. Si je RMB > edit as html sur la balise html et appuyez sur Entrée, tout s'affiche (mais tous les écouteurs d'événements disparaissent). Après avoir lu cette réponse, j'ai changé mes appels createElement en createElementNS et maintenant tout fonctionne !

7 votes

Vous pouvez manipuler les éléments SVG avec jquery une fois qu'ils sont créés à l'aide de la méthode DOM createElementNS. Vous pouvez changer la fonction makeSVG en 'return $(el)' et maintenant vous avez un élément svg qui peut utiliser les méthodes jquery.

0 votes

Comment puis-je utiliser la fonction pour un polygone au lieu d'un cercle, s'il vous plaît.

176voto

Timo Points 2732

El réponse acceptée montre une manière trop compliquée. Comme l'affirme Forresto dans sa réponse , " il semble les ajouter dans l'explorateur DOM, mais pas à l'écran. "La raison en est que les espaces de noms sont différents pour html et svg.

La solution la plus simple consiste à "rafraîchir" l'ensemble du svg. Après avoir ajouté le cercle (ou d'autres éléments), utilisez ceci :

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

Cela fait l'affaire. Le cercle est sur l'écran.

Ou si vous le souhaitez, utilisez un div de conteneur :

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

Et enfermez votre svg dans le div du conteneur :

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

L'exemple fonctionnel :
http://jsbin.com/ejifab/1/edit

Les avantages de cette technique :

  • vous pouvez modifier un svg existant (qui est déjà dans le DOM), par exemple créé à l'aide de Raphael ou comme dans votre exemple "codé en dur" sans script.
  • vous pouvez ajouter des structures d'éléments complexes comme des chaînes de caractères, par exemple. $('svg').prepend('<defs><marker></marker><mask></mask></defs>'); comme vous le faites dans jQuery.
  • après que les éléments aient été ajoutés et rendus visibles à l'écran à l'aide de la fonction $("#cont").html($("#cont").html()); leurs attributs peuvent être modifiés à l'aide de jQuery.

EDITAR:

La technique ci-dessus fonctionne uniquement avec les SVG "codés en dur" ou manipulés par DOM ( = document.createElementNS etc.). Si Raphael est utilisé pour créer des éléments, (selon mes tests) le lien entre les objets Raphael et le DOM SVG est rompu si $("#cont").html($("#cont").html()); est utilisé. La solution de contournement consiste à ne pas utiliser la fonction $("#cont").html($("#cont").html()); au lieu d'utiliser un document SVG factice.

Ce SVG factice est d'abord une représentation textuelle du document SVG et ne contient que les éléments nécessaires. Si nous voulons par exemple ajouter un élément de filtre au document Raphael, le dummy pourrait être quelque chose comme <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg> . La représentation textuelle est d'abord convertie en DOM à l'aide de la méthode $("body").append() de jQuery. Et lorsque l'élément (filtre) est dans le DOM, il peut être interrogé à l'aide des méthodes standard de jQuery et ajouté au document SVG principal qui est créé par Raphael.

Pourquoi cet élément factice est-il nécessaire ? Pourquoi ne pas ajouter un élément de filtre strictement au document créé par Raphael ? Si vous l'essayez en utilisant par exemple $("svg").append("<circle ... />") il est créé comme élément html et rien n'apparaît à l'écran comme décrit dans les réponses. Mais si le document SVG entier est annexé, le navigateur gère automatiquement la conversion de l'espace de nom de tous les éléments du document SVG.

Un exemple permet d'éclairer la technique :

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

Une démonstration complète de cette technique est disponible ici : http://jsbin.com/ilinan/1/edit .

( Je n'ai (encore) aucune idée, pourquoi $("#cont").html($("#cont").html()); ne fonctionne pas quand on utilise Raphael. Ce serait un hack très court. )

0 votes

J'utilise ma solution (maison) pour gérer les SVG et j'ai le même problème que Raphael, lorsque j'utilise le "truc de réparation" avec $(#picture).html ..., mais Ma solution a été de réinitialiser certains éléments SVG mis en cache (rectangle de marquage, transformation, etc.).

0 votes

Vous êtes un magicien. Je regardais les réponses acceptées et je me disais que ça allait être dur. Puis, j'ai vu ça et ça fait tout ce dont j'ai besoin en une seule ligne. Merci !

1 votes

CELA ME FAIT PERDRE MES MARBLES... je pensais que je pouvais magiquement utiliser des js DOM elms sur l'espace de nom SVG comme on pourrait le penser... j'ai réussi à faire reconnaître les insertions par l'inspecteur de balises... mais pas de dés jusqu'à maintenant !

40voto

nategood Points 3753

De plus en plus populaire D3 La bibliothèque gère très bien les bizarreries de l'ajout et de la manipulation des svg. Vous pouvez envisager de l'utiliser plutôt que les bidouillages de jQuery mentionnés ici.

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 votes

Belle réponse. Elle m'a aidé à résoudre certains problèmes.

1 votes

JQuery s'étrangle également en définissant/récupérant des classes à partir de SVG. Juste une parenthèse.

27voto

forresto Points 1783

JQuery ne peut pas ajouter des éléments à <svg> (il semble les ajouter dans l'explorateur DOM, mais pas à l'écran).

Une solution de contournement consiste à ajouter un <svg> avec tous les éléments dont vous avez besoin pour la page, puis modifiez les attributs des éléments à l'aide de la fonction .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 votes

Merci, ça m'a beaucoup aidé ! C'est une très bonne approche. Au lieu d'ajouter <circle> ou d'autres éléments individuellement en utilisant des méthodes DOM complexes et lentes (ex. createDocumentFragment() o createElementNS() ), ajouter le document svg entier dans le conteneur. Au lieu de $('body'), on peut bien sûr utiliser aussi $('div'). Et après cela, le document svg est sur le DOM et peut être interrogé de manière familière en utilisant par exemple $('c').attr('fill','red').

0 votes

J'en ai fait un exemple supplémentaire : jsbin.com/inevaz/1 . Il récupère le code source de tiger.svg d'Internet dans une iframe et il peut être copié-collé dans une zone de texte. Le contenu de la zone de texte est ensuite utilisé comme source pour le svg en ligne qui est ensuite accessible par le DOM (pour changer le style du trait).

0 votes

C'est génial, je ne sais pas pourquoi tout le monde vote pour ces autres réponses qui ne fonctionnent tout simplement pas.

22voto

Chris Dolphin Points 159

Je n'ai pas vu quelqu'un mentionner cette méthode mais document.createElementNS() est utile dans ce cas.

Vous pouvez créer les éléments à l'aide d'un Javascript classique en tant que nœuds DOM normaux avec l'espace de nom correct, puis les qualifier en jQuery à partir de là. Par exemple :

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

Le seul inconvénient est que vous devez créer chaque élément SVG avec le bon espace de nom individuellement, sinon cela ne fonctionnera pas.

4 votes

La réponse de Timo (la plus votée) a fonctionné dans Chrome, mais pas dans IE. Cette réponse a résolu le problème pour les deux navigateurs !

2 votes

Joli ! Bon à savoir

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X