web-dev-qa-db-fra.com

jQuery Selector + SVG incompatible?

J'ai travaillé avec un document HTML5 avec animation SVG et javascript en ligne.

J'aimerais qu'une boîte s'affiche lorsque l'utilisateur clique n'importe où, et j'aimerais qu'elle disparaisse lorsque l'utilisateur clique sur un endroit qui n'est pas la boîte. Cela signifie que je ne peux pas utiliser $(window).click(), ce qui fonctionne.

J'ai essayé de sélectionner les SVG en haut en leur donnant des noms de classe et en utilisant $(".svgclassname").click(), mais cela ne semble pas fonctionner. De même, la sélection de fichiers individuels avec $("#svgname").click().

Quel est le problème?

(Lorsque je remplace $(".eyesvg") par $(window), une boîte bleue apparaît près du curseur lorsque l'utilisateur clique n'importe où dans la fenêtre.)

38
Will

Cela se produit car les spécifications DOM SVG diffèrent beaucoup du DOM HTML.

SVG DOM est un dialecte différent, et certaines propriétés ont les mêmes noms mais signifient des choses différentes. Par exemple, pour obtenir le nom de classe d'un élément svg, vous utilisez:

svg.className.baseVal

Les propriétés affectées par cela sont

className is SVGAnimatedString
height,width, x, y, offsetWidth, offsetHeight are SVGAnimatedLength

Ces propriétés animées sont des structures, avec baseVal tenant la même valeur que vous trouveriez dans HTML DOM et animatedVal tenant je ne sais pas quoi.

SVG DOM manque également certaines bibliothèques de propriétés dépendent, comme innerHTML.

Cela rompt jQuery de plusieurs manières, tout ce qui dépend des propriétés ci-dessus échoue.

En général, SVG DOM et HTML DOM ne se mélangent pas très bien. Ils travaillent ensemble juste assez pour vous attirer, puis les choses se cassent doucement et un autre ange perd ses ailes.

J'ai écrit une petite extension jQuery qui enveloppe les éléments SVG pour les faire ressembler davantage à HTML DOM

(function (jQuery){
    function svgWrapper(el) {
        this._svgEl = el;
        this.__proto__ = el;
        Object.defineProperty(this, "className", {
            get:  function(){ return this._svgEl.className.baseVal; },
            set: function(value){    this._svgEl.className.baseVal = value; }
        });
        Object.defineProperty(this, "width", {
            get:  function(){ return this._svgEl.width.baseVal.value; },
            set: function(value){    this._svgEl.width.baseVal.value = value; }
        });
        Object.defineProperty(this, "height", {
            get:  function(){ return this._svgEl.height.baseVal.value; },
            set: function(value){    this._svgEl.height.baseVal.value = value; }
        });
        Object.defineProperty(this, "x", {
            get:  function(){ return this._svgEl.x.baseVal.value; },
            set: function(value){    this._svgEl.x.baseVal.value = value; }
        });
        Object.defineProperty(this, "y", {
            get:  function(){ return this._svgEl.y.baseVal.value; },
            set: function(value){    this._svgEl.y.baseVal.value = value; }
        });
        Object.defineProperty(this, "offsetWidth", {
            get:  function(){ return this._svgEl.width.baseVal.value; },
            set: function(value){    this._svgEl.width.baseVal.value = value; }
        });
        Object.defineProperty(this, "offsetHeight", {
            get:  function(){ return this._svgEl.height.baseVal.value; },
            set: function(value){    this._svgEl.height.baseVal.value = value; }
        });
    };

    jQuery.fn.wrapSvg = function() {
        return this.map(function(i, el) {
            if (el.namespaceURI == "http://www.w3.org/2000/svg" && !('_svgEl' in el))
                return new svgWrapper(el);
            else
                return el;
            });
        };
})(window.jQuery);

Il crée un wrapper autour des objets SVG qui les fait ressembler à HTML DOM à jQuery. Je l'ai utilisé avec jQuery-UI pour rendre mes éléments SVG largables.

Le manque d'interopérabilité DOM entre HTML et SVG est un désastre total. Toutes les bibliothèques utilitaires douces écrites pour HTML doivent être réinventées pour SVG.

68
Aleksandar Totic

parfois je ne comprends pas ... mais en fait cela ne fonctionne pas avec le sélecteur de classe. Si vous utilisez l'id $ ("# mysvg") ou l'élément $ ("svg") ça marche! Étrange....

Et cela ne fonctionne que lorsque vous déplacez le script onClick de l'en-tête vers le corps après l'élément svg! jquery ne peut lier le onclick que lorsque l'élément est déclaré avant la liaison.

4
räph

vous pouvez utiliser jquery-svg plugin, comme un charme:

<script>
    //get svg object, like a jquery object
    var svg = $("#cars").getSVG();
    //use jquery functions to do some thing
    svg.find("g path:first-child()").attr('fill', color);
</script>
4