web-dev-qa-db-fra.com

jQuery SVG, pourquoi ne puis-je pas ajouterClass?

J'utilise jQuery SVG. Je ne peux pas ajouter ou supprimer une classe à un objet. Quelqu'un sait mon erreur?

Le SVG:

<rect class="jimmy" id="p5" x="200" y="200" width="100" height="100" />

Le jQuery qui n'ajoutera pas la classe:

$(".jimmy").click(function() {
    $(this).addClass("clicked");
});

Je sais que le SVG et jQuery fonctionnent bien parce que je peux je cible l’objet et déclenche une alerte quand on clique dessus:

$(".jimmy").click(function() {
    alert('Handler for .click() called.');
});
281
Don P

Edit 2016: lisez les deux réponses suivantes.

  • JQuery 3 corrige le problème sous-jacent
  • Vanilla JS: element.classList.add('newclass') fonctionne dans les navigateurs modernes

JQuery (moins de 3) ne peut pas ajouter une classe à un SVG.

.attr() fonctionne avec SVG, donc si vous voulez dépendre de jQuery:

// Instead of .addClass("newclass")
$("#item").attr("class", "oldclass newclass");
// Instead of .removeClass("newclass")
$("#item").attr("class", "oldclass");

Et si vous ne voulez pas dépendre de jQuery:

var element = document.getElementById("item");
// Instead of .addClass("newclass")
element.setAttribute("class", "oldclass newclass");
// Instead of .removeClass("newclass")
element.setAttribute("class", "oldclass");
420
forresto

Il y a element.classList dans l'API DOM qui fonctionne à la fois pour les éléments HTML et SVG. Pas besoin de plugin jQuery SVG ni même de jQuery.

$(".jimmy").click(function() {
    this.classList.add("clicked");
});
75
Tomas Mikula

jQuery 3 n'a pas ce problème

L'un des changements répertoriés sur les révisions jQuery 3. est:

ajouter une manipulation de classe SVG ( # 2199 , 20aaed )

Une solution à ce problème serait de passer à jQuery 3. Cela fonctionne très bien:

_var flip = true;
setInterval(function() {
    // Could use toggleClass, but demonstrating addClass.
    if (flip) {
        $('svg circle').addClass('green');
    }
    else {
        $('svg circle').removeClass('green');
    }
    flip = !flip;
}, 1000);_
_svg circle {
    fill: red;
    stroke: black;
    stroke-width: 5;
}
svg circle.green {
    fill: green;
}_
_<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>

<svg>
    <circle cx="50" cy="50" r="25" />
</svg>_

Le problème:

La raison pour laquelle les fonctions de manipulation de classe jQuery ne fonctionnent pas avec les éléments SVG est due au fait que les versions antérieures à la version 3.0 utilisaient la propriété className pour ces fonctions.

Extrait de jQuery attributes/classes.js :

_cur = elem.nodeType === 1 && ( elem.className ?
    ( " " + elem.className + " " ).replace( rclass, " " ) :
    " "
);
_

Cela se comporte comme prévu pour les éléments HTML, mais pour les éléments SVG, className est un peu différent. Pour un élément SVG, className n'est pas une chaîne, mais une instance de SVGAnimatedString .

Considérons le code suivant:

_var test_div = document.getElementById('test-div');
var test_svg = document.getElementById('test-svg');
console.log(test_div.className);
console.log(test_svg.className);_
_#test-div {
    width: 200px;
    height: 50px;
    background: blue;
}_
_<div class="test div" id="test-div"></div>

<svg width="200" height="50" viewBox="0 0 200 50">
  <rect width="200" height="50" fill="green" class="test svg" id="test-svg" />
</svg>_

Si vous exécutez ce code, vous verrez quelque chose comme ce qui suit dans votre console de développement.

_test div
SVGAnimatedString { baseVal="test svg",  animVal="test svg"}
_

Si nous devions transtyper cet objet SVGAnimatedString en une chaîne comme le fait jQuery, nous aurions _[object SVGAnimatedString]_, où jQuery échoue.

Comment le plugin jQuery SVG gère ceci:

Le plug-in jQuery SVG résout ce problème en appliquant des correctifs aux fonctions appropriées pour ajouter le support SVG.

Extrait de jQuery SVG jquery.svgdom.js :

_function getClassNames(elem) {
    return (!$.svg.isSVGElem(elem) ? elem.className :
        (elem.className ? elem.className.baseVal : elem.getAttribute('class'))) || '';
}
_

Cette fonction détectera si un élément est un élément SVG et si c'est le cas, elle utilisera la propriété baseVal de l'objet SVGAnimatedString si disponible, avant de revenir à l'attribut class.

position historique de jQuery sur le sujet:

jQuery répertorie actuellement ce problème sur sa page Won’t Fix . Voici les parties pertinentes.

Bugs SVG/VML ou Namespaced Elements

jQuery est principalement une bibliothèque pour le DOM HTML, de sorte que la plupart des problèmes liés aux documents SVG/VML ou aux éléments namespaced sont hors de portée. Nous essayons de résoudre les problèmes qui se répercutent sur les documents HTML, tels que les événements sortant du format SVG.

Évidemment, jQuery a considéré que la prise en charge complète de SVG ne faisait pas partie du noyau de jQuery et était mieux adaptée aux plugins.

69
Alexander O'Mara

Si vous avez des classes dynamiques ou ne savez pas quelles classes pourraient déjà être appliquées, cette méthode est, à mon avis, la meilleure approche:

// addClass
$('path').attr('class', function(index, classNames) {
    return classNames + ' class-name';
});

// removeClass
$('path').attr('class', function(index, classNames) {
    return classNames.replace('class-name', '');
});
38
nav

Sur la base des réponses ci-dessus, j'ai créé l'API suivante

/*
 * .addClassSVG(className)
 * Adds the specified class(es) to each of the set of matched SVG elements.
 */
$.fn.addClassSVG = function(className){
    $(this).attr('class', function(index, existingClassNames) {
        return ((existingClassNames !== undefined) ? (existingClassNames + ' ') : '') + className;
    });
    return this;
};

/*
 * .removeClassSVG(className)
 * Removes the specified class to each of the set of matched SVG elements.
 */
$.fn.removeClassSVG = function(className){
    $(this).attr('class', function(index, existingClassNames) {
        var re = new RegExp('\\b' + className + '\\b', 'g');
        return existingClassNames.replace(re, '');
    });
    return this;
};
16
Sagar Gala

Après avoir chargé jquery.svg.js, vous devez charger ce fichier: http://keith-wood.name/js/jquery.svgdom.js.

Source: http://keith-wood.name/svg.html#dom

Exemple de travail: http://jsfiddle.net/74RbC/99/

13
bennedich

Ajoutez simplement le constructeur de prototype manquant à tous les nœuds SVG:

SVGElement.prototype.hasClass = function (className) {
  return new RegExp('(\\s|^)' + className + '(\\s|$)').test(this.getAttribute('class'));
};

SVGElement.prototype.addClass = function (className) { 
  if (!this.hasClass(className)) {
    this.setAttribute('class', this.getAttribute('class') + ' ' + className);
  }
};

SVGElement.prototype.removeClass = function (className) {
  var removedClass = this.getAttribute('class').replace(new RegExp('(\\s|^)' + className + '(\\s|$)', 'g'), '$2');
  if (this.hasClass(className)) {
    this.setAttribute('class', removedClass);
  }
};

Vous pouvez ensuite l'utiliser de cette manière sans avoir besoin de jQuery:

this.addClass('clicked');

this.removeClass('clicked');

Tout le crédit va à Todd Moto .

7
Ziad

jQuery 2.2 prend en charge la manipulation de classe SVG

Le jQuery 2.2 et 1.12 publiés post inclut la citation suivante:

Bien que jQuery soit une bibliothèque HTML, nous avons convenu que le support de classe pour les éléments SVG pourrait être utile. Les utilisateurs pourront désormais appeler le . AddClass () , . RemoveClass () , . toggleClass () et . hasClass () méthodes sur SVG. jQuery maintenant modifie l'attribut de classe plutôt que la propriété className . Cela rend également les méthodes de classe utilisables dans les documents XML généraux. Gardez à l'esprit que beaucoup d'autres choses ne fonctionneront pas avec SVG, et nous vous recommandons néanmoins d'utiliser une bibliothèque dédiée à SVG si vous avez besoin de quelque chose de plus que la manipulation de classe.

Exemple utilisant jQuery 2.2.

Il teste:

  • . addClass ()
  • . removeClass ()
  • . hasClass ()

Si vous cliquez sur ce petit carré, sa couleur changera car l'attribut class est ajouté/supprimé.

$("#x").click(function() {
    if ( $(this).hasClass("clicked") ) {
        $(this).removeClass("clicked");
    } else {
        $(this).addClass("clicked");
    }
});
.clicked {
    fill: red !important;  
}
<html>

<head>
    <script src="https://code.jquery.com/jquery-2.2.0.js"></script>
</head>

<body>
    <svg width="80" height="80">
        <rect id="x" width="80" height="80" style="fill:rgb(0,0,255)" />
    </svg>
</body>

</html>
5
ROMANIA_engineer

jQuery ne prend pas en charge les classes d'éléments SVG. Vous pouvez obtenir l'élément directement $(el).get(0) et utiliser classList et add / remove. Cela présente également un truc: le plus haut élément SVG est en fait un objet DOM normal et peut être utilisé comme tout autre élément de jQuery. Dans mon projet, j’ai créé ceci afin de prendre en charge ce dont j’avais besoin, mais la documentation fournie sur le Réseau de développeurs Mozilla comporte un shim qui peut être utilisé comme alternative.

exemple

function addRemoveClass(jqEl, className, addOrRemove) 
{
  var classAttr = jqEl.attr('class');
  if (!addOrRemove) {
    classAttr = classAttr.replace(new RegExp('\\s?' + className), '');
    jqEl.attr('class', classAttr);
  } else {
    classAttr = classAttr + (classAttr.length === 0 ? '' : ' ') + className;
    jqEl.attr('class', classAttr);
  }
}

Une alternative plus difficile consiste à utiliser D3.js comme moteur de sélection. Mes projets ont des graphiques qui sont construits avec elle, c'est donc aussi dans la portée de mon application. D3 modifie correctement les attributs de classe des éléments Vanilla DOM et des éléments SVG. Bien que l'ajout de D3 pour ce cas seulement serait sans doute excessif.

d3.select(el).classed('myclass', true);
5
QueueHammer

Voici mon code plutôt inélégant mais fonctionnel qui traite les problèmes suivants (sans aucune dépendance):

  • classList non existant sur les éléments <svg> dans IE
  • className ne représentant pas l'attribut class sur les éléments <svg> dans IE
  • Les anciennes implémentations de getAttribute() et setAttribute() de l'ancien IE

Il utilise classList si possible.

Code:

var classNameContainsClass = function(fullClassName, className) {
    return !!fullClassName &&
           new RegExp("(?:^|\\s)" + className + "(?:\\s|$)").test(fullClassName);
};

var hasClass = function(el, className) {
    if (el.nodeType !== 1) {
        return false;
    }
    if (typeof el.classList == "object") {
        return (el.nodeType == 1) && el.classList.contains(className);
    } else {
        var classNameSupported = (typeof el.className == "string");
        var elClass = classNameSupported ? el.className : el.getAttribute("class");
        return classNameContainsClass(elClass, className);
    }
};

var addClass = function(el, className) {
    if (el.nodeType !== 1) {
        return;
    }
    if (typeof el.classList == "object") {
        el.classList.add(className);
    } else {
        var classNameSupported = (typeof el.className == "string");
        var elClass = classNameSupported ?
            el.className : el.getAttribute("class");
        if (elClass) {
            if (!classNameContainsClass(elClass, className)) {
                elClass += " " + className;
            }
        } else {
            elClass = className;
        }
        if (classNameSupported) {
            el.className = elClass;
        } else {
            el.setAttribute("class", elClass);
        }
    }
};

var removeClass = (function() {
    function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
        return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
    }

    return function(el, className) {
        if (el.nodeType !== 1) {
            return;
        }
        if (typeof el.classList == "object") {
            el.classList.remove(className);
        } else {
            var classNameSupported = (typeof el.className == "string");
            var elClass = classNameSupported ?
                el.className : el.getAttribute("class");
            elClass = elClass.replace(new RegExp("(^|\\s)" + className + "(\\s|$)"), replacer);
            if (classNameSupported) {
                el.className = elClass;
            } else {
                el.setAttribute("class", elClass);
            }
        }
    }; //added semicolon here
})();

Exemple d'utilisation:

var el = document.getElementById("someId");
if (hasClass(el, "someClass")) {
    removeClass(el, "someClass");
}
addClass(el, "someOtherClass");
3
Tim Down

J'utilise Snap.svg pour ajouter une classe à et SVG.

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

http://snapsvg.io/docs/#Element.addClass

2
cjohndesign

Une solution de contournement pourrait être d'ajouterClass à un conteneur de l'élément svg:

$('.svg-container').addClass('svg-red');
.svg-red svg circle{
    fill: #ED3F32;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="svg-container">
  <svg height="40" width="40">
      <circle cx="20" cy="20" r="20"/>
  </svg>
</div>
1
claytronicon

Inspiré par les réponses ci-dessus, en particulier par Sagar Gala, j'ai créé ceci API . Vous pouvez l'utiliser si vous ne voulez pas ou ne pouvez pas mettre à jour votre version de jQuery.

0
Stonecrusher

Je l'ai écrit dans mon projet, et ça marche ... probablement;)

$.fn.addSvgClass = function(className) {

    var attr
    this.each(function() {
        attr = $(this).attr('class')
        if(attr.indexOf(className) < 0) {
            $(this).attr('class', attr+' '+className+ ' ')
        }
    })
};    

$.fn.removeSvgClass = function(className) {

    var attr
    this.each(function() {
        attr = $(this).attr('class')
        attr = attr.replace(className , ' ')
        $(this).attr('class' , attr)
    })
};    

exemples

$('path').addSvgClass('fillWithOrange')
$('path').removeSvgClass('fillWithOrange')
0
Dariusz Majchrzak