jQuery SVG, warum kann ich keine Klasse hinzufügen?

289

Ich benutze jQuery SVG. Ich kann einem Objekt keine Klasse hinzufügen oder entfernen. Kennt jemand meinen Fehler?

Die SVG:

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

Die jQuery, die die Klasse nicht hinzufügt:

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

Ich weiß , dass die SVG und jQuery arbeiten zusammen in Ordnung , weil ich kann das Objekt zielen und einen Alarm ausgelöst , wenn es angeklickt wird :

$(".jimmy").click(function() {
    alert('Handler for .click() called.');
});
Don P.
quelle
4
Guter Artikel hier: toddmotto.com/…
Kris
8
Die eigentliche Frage, WARUM ich die Funktionen der jQuery * -Klasse nicht verwenden kann, bleibt unbeantwortet. Wenn ich über die Funktion jQuery attr auf die Eigenschaften von SVG-Elementen zugreifen kann, warum können die Funktionen der * Klasse dies nicht auch? jQuery FAIL?!?
Ryan Griggs
2
Im Ernst, weiß jemand warum ?
Ben Wilde
Winzige Bibliothek, die diese Funktionalität für SVG-Dateien hinzufügt: github.com/toddmotto/lunar
Chuck Le Butt
6
Das "Warum" liegt daran, dass jQuery die Eigenschaft "className" verwendet, eine Zeichenfolgeeigenschaft für HTML-Elemente, aber eine animierte SVG-Zeichenfolge für SVG-Elemente. Welches kann nicht zugewiesen werden, um für HTML-Elemente zu mögen. Daher wird der jQuery-Code beim Versuch, den Wert zuzuweisen, in die Luft gesprengt.
Jon Watte

Antworten:

426

Edit 2016: Lesen Sie die nächsten beiden Antworten.

  • JQuery 3 behebt das zugrunde liegende Problem
  • Vanilla JS: element.classList.add('newclass')funktioniert in modernen Browsern

JQuery (weniger als 3) kann einer SVG keine Klasse hinzufügen.

.attr() funktioniert mit SVG. Wenn Sie sich also auf jQuery verlassen möchten:

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

Und wenn Sie sich nicht auf jQuery verlassen möchten:

var element = document.getElementById("item");
// Instead of .addClass("newclass")
element.setAttribute("class", "oldclass newclass");
// Instead of .removeClass("newclass")
element.setAttribute("class", "oldclass");
forresto
quelle
Zur Unterstützung des Forresto-Vorschlags - Diskutieren
Sie
perfekt. Das sollte die Antwort sein. obwohl die WIRKLICHE Antwort lautet, dass jquery dies standardmäßig einschließt, zumindest als Einstellung oder so.
AwokeKnowing
2
.attr("class", "oldclass")(oder umso umständlicher .setAttribute("class", "oldclass")) ist wirklich nicht gleichbedeutend mit Entfernen newclass!
Tom
Tolle Antwort (und sehr gute Technik) ... aber wäre es eine Idee, die Frage so zu bearbeiten, dass zunächst festgestellt wird, dass jquery einer SVG keine Klasse hinzufügen kann (wenn dies der Fall ist ... wie es scheint) )?
Byronyasgur
1
Nur ein Nachtrag: Ich habe es versucht und JQuery 3 hat das Problem nicht behoben.
Joao Arruda
76

In der DOM-API befindet sich element.classList, das sowohl für HTML- als auch für SVG-Elemente funktioniert. Kein jQuery SVG Plugin oder sogar jQuery erforderlich.

$(".jimmy").click(function() {
    this.classList.add("clicked");
});
Tomas Mikula
quelle
3
Ich habe dies getestet und .classList scheint für SVG-Unterelemente undefiniert zu sein, zumindest in Chrome.
Chris Jaynes
3
Funktioniert für mich in Chrome. Ich habe SVG in HTML eingebettet, daher sieht meine Dokumentstruktur folgendermaßen aus: <html> <svg> <circle class = "jimmy" ...> </ svg> </ html>
Tomas Mikula
1
Ich habe dies verwendet, um eine Klasse in ein <g> SVG-Element einzufügen. Funktioniert in Chrome einwandfrei.
Rachelle Uy
20
IE implementiert .classList und API nicht in SVG-Elementen. Siehe caniuse.com/#feat=classlist und die Liste "Bekannte Probleme". Das erwähnt auch WebKit-Browser.
Shenme
9
Und wie die Zeit vergeht, wird diese Antwort noch mehr korrekt (und die beste Antwort)
Gerrat
69

jQuery 3 hat dieses Problem nicht

Eine der in den jQuery 3.0-Revisionen aufgeführten Änderungen ist:

SVG-Klassenmanipulation hinzufügen ( # 2199 , 20aaed3 )

Eine Lösung für dieses Problem wäre ein Upgrade auf jQuery 3. Es funktioniert hervorragend:

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>


Das Problem:

Der Grund, warum die Manipulationsfunktionen der jQuery-Klasse nicht mit den SVG-Elementen funktionieren, liegt darin, dass jQuery-Versionen vor 3.0 die classNameEigenschaft für diese Funktionen verwenden.

Auszug aus jQuery attributes/classes.js:

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

Dies verhält sich für HTML-Elemente wie erwartet, für SVG-Elemente classNamejedoch etwas anders. Ist für ein SVG-Element classNamekeine Zeichenfolge, sondern eine Instanz von SVGAnimatedString.

Betrachten Sie den folgenden Code:

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>

Wenn Sie diesen Code ausführen, wird in Ihrer Entwicklerkonsole Folgendes angezeigt.

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

Wenn wir dieses SVGAnimatedStringObjekt wie jQuery in einen String umwandeln würden, hätten wir [object SVGAnimatedString], wo jQuery fehlschlägt.

Wie das jQuery SVG-Plugin damit umgeht:

Das jQuery SVG-Plugin umgeht dies, indem es die relevanten Funktionen patcht, um SVG-Unterstützung hinzuzufügen.

Auszug aus jQuery SVG jquery.svgdom.js:

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

Diese Funktion erkennt, ob ein Element ein SVG-Element ist, und verwendet, falls verfügbar, die baseValEigenschaft des SVGAnimatedStringObjekts, falls verfügbar, bevor auf das classAttribut zurückgegriffen wird.

jQuerys historische Haltung zu diesem Thema:

jQuery listet dieses Problem derzeit auf der Seite " Nicht behoben " auf. Hier sind die relevanten Teile.

Fehler bei SVG / VML- oder Namespaced-Elementen

jQuery ist in erster Linie eine Bibliothek für das HTML-DOM, daher liegen die meisten Probleme im Zusammenhang mit SVG / VML-Dokumenten oder Elementen mit Namespace außerhalb des Gültigkeitsbereichs. Wir versuchen, Probleme zu beheben, die in HTML-Dokumenten "durchbluten", z. B. Ereignisse, die aus SVG heraussprudeln.

Offensichtlich hat jQuery die vollständige SVG-Unterstützung außerhalb des Bereichs des jQuery-Kerns in Betracht gezogen und ist besser für Plugins geeignet.

Alexander O'Mara
quelle
38

Wenn Sie dynamische Klassen haben oder nicht wissen, welche Klassen bereits angewendet werden könnten, ist diese Methode meiner Meinung nach der beste Ansatz:

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

// removeClass
$('path').attr('class', function(index, classNames) {
    return classNames.replace('class-name', '');
});
nav
quelle
1
Dies war ein Lebensretter für mich, da ich viele SVG-Elemente ändern musste, um Dinge zu erledigen. +999 von mir :)
Karl
Es ist kaum zu glauben, dass sich jQuery auch nach 2 Jahren und der Veröffentlichung von 2.xx immer noch daran gewöhnt hat. Ihr Fix, nav, hat mir den Tag gerettet. Der Rest dieser Korrekturen war unnötig komplex. Vielen Dank!
konkurrenzlose
17

Basierend auf den obigen Antworten habe ich die folgende API erstellt

/*
 * .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;
};
Sagar Gala
quelle
3
Dies ist hilfreich, aber die Algorithmen haben folgende Probleme: 1. Wenn keine Klassenzeichenfolge vorhanden ist, erhalten Sie beim Hinzufügen einer Klasse 'undefinierten Klassennamen'. 2. Wenn die Klassenzeichenfolge eine Klasse enthält, die die Obermenge des regulären Ausdrucks darstellt, hinterlassen Sie Junk in der Klassenzeichenfolge. Wenn Sie beispielsweise versuchen, die Klasse 'Hund' zu entfernen, und die Klassenzeichenfolge 'Hundefutter' enthält, bleibt in der Klassenzeichenfolge 'Futter'. Hier sind einige Korrekturen: jsfiddle.net/hellobrett/c2c2zfto
Brett
1
Dies ist keine perfekte Lösung. Es ersetzt alle Wörter, die eine Klasse enthalten, die Sie entfernen möchten.
Tom10271
13

Nach dem Laden jquery.svg.jsmüssen Sie diese Datei laden : http://keith-wood.name/js/jquery.svgdom.js.

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

Arbeitsbeispiel: http://jsfiddle.net/74RbC/99/

bennedich
quelle
Hinzugefügt - scheint immer noch nicht zu funktionieren. Ich habe die Dokumentation auf der Website von jquery SVG durchgesehen, aber es gibt keine klare Erklärung dafür, was Sie tun müssen, um die zusätzliche Funktionalität zu nutzen.
Don P
7

Fügen Sie einfach den fehlenden Prototypkonstruktor zu allen SVG-Knoten hinzu:

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

Sie können es dann auf diese Weise verwenden, ohne dass jQuery erforderlich ist:

this.addClass('clicked');

this.removeClass('clicked');

Alle Kredite gehen an Todd Moto .

Ziad
quelle
dh11 Drosseln an diesem Code. Diese Polyfüllung war eine bessere Route: github.com/eligrey/classList.js
Ryanrain
5

jQuery unterstützt die Klassen von SVG-Elementen nicht. Sie können das Element direkt $(el).get(0)abrufen classListund und verwenden add / remove. Dies hat auch den Trick, dass das oberste SVG-Element tatsächlich ein normales DOM-Objekt ist und wie jedes andere Element in jQuery verwendet werden kann. In meinem Projekt habe ich dies erstellt, um mich um das zu kümmern, was ich brauchte, aber die Dokumentation im Mozilla Developer Network enthält eine Unterlegscheibe, die als Alternative verwendet werden kann.

Beispiel

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

Eine härtere Alternative ist die Verwendung von D3.js als Selector-Engine. Meine Projekte haben Diagramme, die damit erstellt wurden, sodass sie auch in meinem App-Bereich enthalten sind. D3 ändert die Klassenattribute von Vanille-DOM-Elementen und SVG-Elementen korrekt. Das Hinzufügen von D3 für genau diesen Fall wäre wahrscheinlich übertrieben.

d3.select(el).classed('myclass', true);
QueueHammer
quelle
1
Danke für das d3-Bit ... Ich hatte tatsächlich schon d3 auf der Seite, also habe ich mich einfach dafür entschieden. Sehr nützlich :)
Muers
5

jQuery 2.2 unterstützt die Manipulation von SVG-Klassen

Der veröffentlichte Beitrag zu jQuery 2.2 und 1.12 enthält das folgende Zitat:

Während jQuery eine HTML-Bibliothek ist, waren wir uns einig, dass die Klassenunterstützung für SVG-Elemente nützlich sein könnte. Benutzer können jetzt die Methoden .addClass () , .removeClass () , .toggleClass () und .hasClass () in SVG aufrufen . jQuery ändert jetzt das Klassenattribut und nicht die className-Eigenschaft . Dadurch können die Klassenmethoden auch in allgemeinen XML-Dokumenten verwendet werden. Denken Sie daran, dass viele andere Dinge mit SVG nicht funktionieren. Wir empfehlen dennoch, eine Bibliothek zu verwenden, die SVG gewidmet ist, wenn Sie etwas anderes als Klassenmanipulation benötigen.

Beispiel mit jQuery 2.2.0

Es testet:

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

Wenn Sie auf dieses kleine Quadrat klicken, ändert es seine Farbe, da das classAttribut hinzugefügt / entfernt wird.

$("#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>

ROMANIA_engineer
quelle
3

Hier ist mein eher uneleganter, aber funktionierender Code, der sich mit den folgenden Problemen befasst (ohne Abhängigkeiten):

  • classListNicht vorhanden für <svg>Elemente im IE
  • classNameNicht das classAttribut für <svg>Elemente im IE darstellen
  • Alter IE ist kaputt getAttribute()und setAttribute()Implementierungen

Es verwendet classListwo möglich.

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

Anwendungsbeispiel:

var el = document.getElementById("someId");
if (hasClass(el, "someClass")) {
    removeClass(el, "someClass");
}
addClass(el, "someOtherClass");
Tim Down
quelle
Haben Sie es mit IE7 getestet? Da für LTE IE7 strAttributeName "className" sein muss, wenn ein CLASS-Attribut festgelegt, abgerufen oder entfernt wird .
Knu
@Knu: Es funktioniert definitiv in IE 6 und 7 für reguläre HTML-Elemente, da in diesen Browsern die classNameEigenschaft verwendet wird, anstatt zu versuchen, ein Attribut festzulegen . Der Grund dafür, dass "LTE IE7 beim Festlegen, Abrufen oder Entfernen eines CLASS-Attributs erfordert, dass strAttributeName" className "ist, liegt darin, dass diese Browser eine fehlerhafte Implementierung von haben getAttribute(x)und setAttribute(x)die Eigenschaft einfach mit name abrufen oder festlegen x, sodass sie einfacher und kompatibler ist um die Eigenschaft direkt festzulegen.
Tim Down
@ Knu: OK. Ich war mir nicht sicher, da SVG in IE 7 nicht unterstützt wird <svg>. Daher bin ich mir nicht sicher, was Sie erreichen möchten, wenn Sie ein Element in eine Seite aufnehmen, die für IE 7 geeignet ist. Für das, was es wert ist, meinen Code Fügt <svg>Elementen in IE 7 erfolgreich ein Klassenattribut hinzu, da sich ein solches Element wie jedes andere benutzerdefinierte Element verhält.
Tim Down
Völlig vergessen, danke für die Erinnerung. Ich werde versuchen, Adobe SVG Viewer in meine Hände zu bekommen, um zu überprüfen, ob es wie beabsichtigt funktioniert.
Knu
2

Ich benutze Snap.svg, um eine Klasse zu und SVG hinzuzufügen.

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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

cjohndesign
quelle
1
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Tristan
1

Eine Problemumgehung könnte darin bestehen, einem Container des svg-Elements eine Klasse hinzuzufügen:

$('.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>

Claytronicon
quelle
1

Ich habe das in meinem Projekt geschrieben und es funktioniert ... wahrscheinlich;)

$.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)
    })
};    

Beispiele

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

Inspiriert von den obigen Antworten, insbesondere von Sagar Gala, habe ich diese API erstellt . Sie können es verwenden, wenn Sie Ihre jquery-Version nicht aktualisieren möchten oder können.

Steinbrecher
quelle
-1

Oder verwenden Sie einfach DOM-Methoden der alten Schule, wenn JQ irgendwo einen Affen in der Mitte hat.

var myElement = $('#my_element')[0];
var myElClass = myElement.getAttribute('class').split(/\s+/g);
//splits class into an array based on 1+ white space characters

myElClass.push('new_class');

myElement.setAttribute('class', myElClass.join(' '));

//$(myElement) to return to JQ wrapper-land

Lerne die DOM-Leute. Auch im Framework-Palooza 2016 hilft es ganz regelmäßig. Wenn Sie jemals jemanden hören, der das DOM mit der Montage vergleicht, treten Sie ihn für mich.

Erik Reppen
quelle