jQuery SVG, neden addClass ekleyemiyorum?


289

JQuery SVG kullanıyorum. Bir nesneye sınıf ekleyemiyorum veya sınıftan kaldıramıyorum. Benim hatamı bilen var mı?

SVG:

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

Sınıfı eklemeyecek jQuery:

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

Ben çünkü SVG ve jQuery birlikte iyi çalışıyor biliyor olabilir nesneyi hedef ve tıklandığında bir uyarı yangın:

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

4
Burada iyi bir yazı: toddmotto.com/…
Kris

8
Fiili soru Yani NEDEN jQuery * sınıf işlevlerini kullanamaz cevapsız kalmaktadır ... Ben jQuery attr fonksiyonuyla SVG öğelerinin özelliklerini erişebilir, neden * sınıf işlevleri de bunu yapamaz? jQuery BAŞARISIZ?!?
Ryan Griggs

2
Cidden, kimse neden biliyor mu ??
Ben Wilde

SVG dosyaları için bu işlevselliği ekleyen küçük kütüphane: github.com/toddmotto/lunar
Chuck Le Butt

6
"Neden" jQuery, HTML öğeleri için bir dize özelliği olan ancak SVG öğeleri için bir SVG Animasyonlu Dizesi olan "className" özelliğini kullanmasıdır. HTML öğeleri için beğenilemez. Böylece, jQuery kodu değeri atamaya çalışırken patlar.
Jon Watte

Yanıtlar:


426

2016'yı Düzenle: sonraki iki yanıtı okuyun.

  • JQuery 3, temel sorunu giderir
  • Vanilla JS: element.classList.add('newclass')Modern tarayıcılarda çalışır

JQuery (3'ten az) bir SVG'ye sınıf ekleyemez.

.attr() SVG ile çalışır, bu nedenle jQuery'ye bağımlı olmak istiyorsanız:

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

Ve jQuery'ye bağımlı olmak istemiyorsanız:

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

Forresto önerisine destek olarak - tartışmakcode.blogspot.in/2012/08/…
helloworld

mükemmel. cevap bu olmalı. GERÇEK yanıtı, jquery'nin bunu varsayılan olarak en azından bir ayar veya bir şey olarak dahil etmesi içindir.
AwokeKnowing

2
.attr("class", "oldclass")(veya daha hantal .setAttribute("class", "oldclass")) gerçekten kaldırmaya eşdeğer değildir newclass!
Tom

Harika cevap (ve v.nice tekniği) ... ancak soruyu düzenlemek, jquery'nin SVG'ye bir sınıf ekleyemediğini belirterek bir fikir olur mu? )?
byronyasgur

1
Sadece bir zeyilname: Denedim ve JQuery 3 sorunu çözmedi.
Joao Arruda

76

DOM API'sinde hem HTML hem de SVG öğeleri için çalışan element.classList vardır . JQuery SVG eklentisine ve hatta jQuery'ye gerek yok.

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

3
Bunu test ettim ve .classList, en azından Chrome'da, svg alt öğeleri için tanımsız görünüyor.
Chris Jaynes

3
Chrome'da benim için çalışıyor. HTML'ye gömülü SVG'im var, bu nedenle belge yapım şöyle görünüyor: <html> <svg> <circle class = "jimmy" ...> </svg> </html>
Tomas Mikula

1
Bunu bir <g> SVG öğesinde sınıf eklemek için kullandım, Chrome'da iyi çalışıyor.
Rachelle Uy

20
IE, SVG Elements üzerinde .classList ve API uygulamaz. Bkz caniuse.com/#feat=classlist ve "bilinen sorunlar" listesi. Bu WebKit tarayıcılarından da bahsediyor.
Shenme

9
Zaman geçtikçe Ve bu cevap bile olur daha doğru (ve en iyi cevap)
Gerrat

69

jQuery 3'te bu sorun yok

JQuery 3.0 revizyonlarında listelenen değişikliklerden biri:

SVG sınıfı manipülasyonu ekle ( # 2199 , 20aaed3 )

Bu sorunun bir çözümü jQuery 3'e yükseltmek olacaktır. Harika çalışıyor:

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>


Sorun:

JQuery sınıfı düzenleme işlevlerinin SVG öğeleriyle çalışmamasının nedeni, 3.0'dan önceki jQuery sürümlerinin classNamebu işlevler için özelliği kullanmasıdır .

JQuery'den alıntı attributes/classes.js:

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

Bu, HTML öğeleri için beklendiği gibi davranır, ancak SVG öğeleri classNameiçin biraz farklıdır. Bir SVG öğesi için, classNamebir dize değil, bir örneğidir SVGAnimatedString.

Aşağıdaki kodu göz önünde bulundurun:

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>

Bu kodu çalıştırırsanız geliştirici konsolunuzda aşağıdakine benzer bir şey görürsünüz.

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

Bu SVGAnimatedStringnesneyi jQuery'nin yaptığı gibi bir dizeye atacak olsaydık [object SVGAnimatedString], jQuery'nin başarısız olduğu yer olurdu .

JQuery SVG eklentisi bunu nasıl ele alır:

JQuery SVG eklentisi, SVG desteği eklemek için ilgili işlevleri yamalayarak bu sorunu giderir.

JQuery SVG'den alıntı jquery.svgdom.js:

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

Bir öğe bir SVG unsurudur Bu fonksiyon algılar ve eğer o kullanacağı baseValözelliği SVGAnimatedStringvarsa geri düşmeden önce, nesnenin classözniteliği.

jQuery'nin bu konudaki tarihsel duruşu:

jQuery şu anda bu sorunu Düzeltilmeyecek sayfasında listeliyor . İşte ilgili parçalar.

SVG / VML veya Ad Aralığı Öğeleri Hataları

jQuery öncelikle HTML DOM için bir kütüphanedir, bu nedenle SVG / VML belgeleri veya ad boşluklu öğelerle ilgili sorunların çoğu kapsam dışıdır. SVG dışına çıkan olaylar gibi HTML belgelerine "sızan" sorunları çözmeye çalışıyoruz.

Açıkçası jQuery, jQuery çekirdeğinin kapsamı dışında tam SVG desteği olarak kabul edildi ve eklentiler için daha uygun.


38

Dinamik sınıflarınız varsa veya hangi sınıfların zaten uygulanabileceğini bilmiyorsanız, bu yöntem en iyi yaklaşım olduğuna inanıyorum:

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

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

1
İşleri halletmek için birçok SVG öğesini değiştirmem gerektiğinden bu benim için bir cankurtaran oldu. Benden +999 :)
Karl

JQuery'nin 2 yıl sonra ve 2.xx'in yayınlanmasından sonra bile topuklarını bu şekilde kazacağına inanmak zor. Düzeltme, nav, günü benim için kurtardı. Bu düzeltmelerin geri kalanı gereksiz yere karmaşıktı. Teşekkürler!
rakipsiz

17

Yukarıdaki cevaplara dayanarak aşağıdaki API'yi oluşturdum

/*
 * .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;
};

3
Bu yardımcı olur, ancak algoritmaların sorunları vardır: 1. Var olan bir sınıf dizesi yoksa, bir sınıf ekledikten sonra 'tanımlanmamış className' alırsınız. 2. Sınıf dizesi, normal ifadenin üst kümesi olan bir sınıf içeriyorsa, sınıf dizesinde önemsiz olarak geride kalırsınız. Örneğin, 'dog' sınıfını kaldırmaya çalışırsanız ve sınıf dizesi 'dogfood' içeriyorsa, sınıf dizesinde 'food' ifadesi kalır. İşte bazı düzeltmeler: jsfiddle.net/hellobrett/c2c2zfto
Brett

1
Bu mükemmel bir çözüm değil. Kaldırmak istediğiniz sınıfı içeren tüm kelimeleri değiştirir.
tom10271

13

Yükleme işleminden sonra jquery.svg.jsbu dosyayı yüklemek gerekir: http://keith-wood.name/js/jquery.svgdom.js.

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

Çalışma örneği: http://jsfiddle.net/74RbC/99/


Ekledi - hala çalışmıyor gibi görünüyor. Jquery SVG'nin sitesindeki belgelere baktım, ancak ekstra işlevselliğinden yararlanmak için ne yapmanız gerektiği konusunda net bir açıklama yok.
Don P

7

Eksik olan prototip yapıcısını tüm SVG düğümlerine eklemeniz yeterlidir:

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

Daha sonra jQuery gerektirmeden bu şekilde kullanabilirsiniz:

this.addClass('clicked');

this.removeClass('clicked');

Tüm kredi Todd Moto'ya gidiyor .


ie11 bu kodu boğar. bu çoklu dolgu
ryanrain

5

jQuery, SVG öğelerinin sınıflarını desteklemez. Sen elemanı doğrudan ulaşabilirler $(el).get(0)ve kullanımı classListve add / remove. En üstteki SVG öğesinin aslında normal bir DOM nesnesi olması ve jQuery'deki diğer tüm öğeler gibi kullanılabilmesi açısından bununla ilgili bir hile vardır. Projemde bunu ihtiyacım olan şeylerle ilgilenmek için yarattım, ancak Mozilla Geliştirici Ağı'nda sağlanan belgelerin alternatif olarak kullanılabilecek bir şimi var.

misal

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

Tüm bunlardan daha zor bir alternatif ise, seçici motorunuz olarak D3.js'yi kullanmaktır. Projelerim onunla oluşturulmuş grafikler içeriyor, bu yüzden uygulama kapsamımda da var. D3, vanilya DOM öğelerinin ve SVG öğelerinin sınıf özniteliklerini doğru şekilde değiştirir. Sadece bu durum için D3 eklemek aşırıya kaçabilir.

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

1
Bu d3 bit için teşekkürler ... Aslında zaten sayfada d3 vardı, bu yüzden sadece kullanmaya karar verdi. Çok yararlı :)
Muers

5

jQuery 2.2, SVG sınıfı manipülasyonunu destekler

JQuery 2.2 ve 1.12 Çıkış sonrası şu alıntı içerir:

JQuery bir HTML kitaplığı olsa da, SVG öğeleri için sınıf desteğinin yararlı olabileceğini kabul ettik. Kullanıcılar artık SVG'de .addClass () , .removeClass () , .toggleClass () ve .hasClass () yöntemlerini çağırabilecektir . jQuery artık className özelliği yerine class niteliğini değiştiriyor . Bu, sınıf yöntemlerini genel XML belgelerinde de kullanılabilir hale getirir. Diğer birçok şeyin SVG ile çalışmayacağını ve sınıf manipülasyonunun ötesinde bir şeye ihtiyacınız varsa SVG'ye adanmış bir kütüphane kullanmanızı öneririz.

JQuery 2.2.0 kullanan örnek

Test eder:

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

Bu küçük kareyi tıklarsanız, classözellik eklendiği / kaldırıldığı için rengini değiştirir .

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


3

İşte, (herhangi bir bağımlılık olmadan) aşağıdaki sorunlarla ilgilenen oldukça yetersiz ama çalışma kodum:

  • classList<svg>IE'deki öğelerde mevcut değil
  • classNameIE'deki öğelerdeki classözniteliği temsil etmiyor<svg>
  • Eski IE'ler bozuldu getAttribute()ve setAttribute()uygulamaları

classListMümkün olan yerlerde kullanır .

Kod:

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

Örnek kullanım:

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

IE7 kullanarak test ettiniz mi? LTE IE7 , bir CLASS özniteliğini ayarlarken, alırken veya kaldırırken strAttributeName öğesinin "className" olmasını gerektirdiğinden .
Knu

@Knu: IE 6 ve 7'de normal HTML öğeleri için kesinlikle çalışır, çünkü bu tarayıcılarda classNamebir özellik ayarlamaya çalışmak yerine özelliği kullanır . "LTE IE7'nin bir CLASS özniteliğini ayarlarken, alırken veya kaldırırken strAttributeName öğesinin" className "olmasını gerektirmesinin nedeni, bu tarayıcıların bozuk bir uygulamasına sahip olması getAttribute(x)ve setAttribute(x)özelliği adıyla alması veya ayarlamasıdır x, bu nedenle daha kolay ve daha uyumludur özelliği doğrudan ayarlamak için.
Tim Down

@Knu: Tamam. IE 7'de SVG desteklenmediğinden emin değildim, bu yüzden <svg>IE 7'de çalışmak üzere tasarlanmış bir sayfaya bir öğe ekleyerek ne elde etmeyi umduğunuzdan emin değilim. <svg>böyle bir öğe diğer herhangi bir özel öğe gibi davrandığından IE 7'deki öğelere başarıyla bir sınıf özniteliği ekler .
Tim Down

Tamamen unuttum hatırlatma için teşekkürler. İstenen şekilde çalışıp çalışmadığını kontrol etmek için Adobe SVG Viewer'a ellerimi sokmaya çalışacağım.
Knu

2

SVG ve sınıf eklemek için Snap.svg kullanın.

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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


1
Bu bağlantı soruyu cevaplayabilse de, cevabın temel kısımlarını buraya eklemek ve bağlantıyı referans olarak sağlamak daha iyidir. Bağlantı verilen sayfa değişirse, yalnızca bağlantı yanıtları geçersiz olabilir.
Tristan

1

Bir çözüm, svg öğesinin bir kapsayıcısına addClass eklemek olabilir:

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

Bunu projeme yazdım ve işe yarıyor ... muhtemelen;)

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

örnekler

$('path').addSvgClass('fillWithOrange')
$('path').removeSvgClass('fillWithOrange')

0

Yukarıdaki yanıtlardan, özellikle de Sagar Gala'dan esinlenerek bu API'yı oluşturdum . Jquery sürümünüzü istemiyorsanız veya yükseltemiyorsanız kullanabilirsiniz.


-1

Veya JQ'nun ortasında bir maymun olduğunda eski okul DOM yöntemlerini kullanın.

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

DOM çalışanlarını öğrenin. 2016'daki çerçeve-paloozada bile oldukça düzenli olarak yardımcı olur. Ayrıca, birinin DOM'u montajla karşılaştırdığını duyduysanız, benim için tekmeleyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.