jQuery SVG、なぜaddClassができないのですか?


289

私はjQuery SVGを使用しています。オブジェクトのクラスを追加または削除できません。誰かが私の間違いを知っていますか?

SVG:

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

クラスを追加しないjQuery:

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

SVGとjQueryがうまく連携していることはわかっています。オブジェクトをターゲットにでき、クリックされたときにアラートを発生させることができるからです。

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

4
ここでは良い記事:toddmotto.com/...
クリス

8
したがって、なぜ jQuery * class関数を使用できないのかという実際の質問は未回答のままです... jQuery attr関数を介してSVG要素のプロパティにアクセスできる場合、なぜ* class関数もこれを実行できないのですか?jQuery FAIL?!?
Ryan Griggs、2014

2
真剣に、なぜ誰もが知っていますか?
ベンワイルド

SVGファイルにこの機能を追加する小さなライブラリ:github.com/toddmotto/lunar
Chuck Le Butt

6
「理由」は、jQueryが「className」プロパティを使用するためです。これは、HTML要素の文字列プロパティですが、SVG要素のSVGアニメーション文字列です。HTML要素の「いいね」に割り当てることはできません。したがって、jQueryコードは、値を割り当てようとして爆破します。
Jon Watte、2016年

回答:


426

編集2016:次の2つの答えを読んでください。

  • jQuery 3は根本的な問題を修正します
  • Vanilla JS:element.classList.add('newclass')最新のブラウザで動作します

JQuery(3未満)はSVGにクラスを追加できません。

.attr() SVGで動作するので、jQueryに依存したい場合:

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

そして、jQueryに依存したくない場合:

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

- forresto提案への支援にdiscusscode.blogspot.in/2012/08/...
HelloWorldの

完璧。これが答えになるはずです。実際の答えは、jqueryがこれをデフォルトで、少なくとも設定などとして含めることです。
2013年

2
.attr("class", "oldclass")(またはより厄介な.setAttribute("class", "oldclass"))は、削除することと実際には同じではありませんnewclass
トム

素晴らしい答え(そしてv.niceテクニック)...しかし、jqueryがSVGにクラスを追加できないことを示すことから始めるように質問を編集することは考えられます(そうである場合... )?
byronyasgur 2014

1
単なる補遺:私が試したが、JQuery 3は問題を修正しなかった。
Joao Arruda

76

DOM APIには、HTML要素とSVG要素の両方で機能するelement.classListがあります。jQuery SVGプラグインやjQueryも必要ありません。

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

3
私はこれをテストしましたが、少なくともChromeでは、.classListがsvgサブ要素に対して未定義のようです。
Chris Jaynes、2013

3
Chromeで動作します。HTMLにSVGが埋め込まれているので、ドキュメント構造は次のようになります。<html> <svg> <circle class = "jimmy" ...> </ svg> </ html>
Tomas Mikula 2013年

1
これを使用して<g> SVG要素にクラスを追加し、Chromeで正常に動作します。
Rachelle Uy 2013

20
IEはSVG要素に.classListとAPIを実装していません。caniuse.com/#feat=classlistと「既知の問題」リストを参照してください。WebKitブラウザーについても触れています。
Shenme 2014

9
そして時間が経つにつれ、この答えはさらに正確になります(そして最良の答え)
Gerrat

69

jQuery 3にはこの問題はありません

jQuery 3.0リビジョンにリストされいる変更の1つは次のとおりです。

SVGクラス操作追加(#2199を20aaed3

この問題の1つの解決策は、jQuery 3にアップグレードすることです。

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>


問題:

jQueryクラス操作関数がSVG要素で機能しないのは、3.0より前のバージョンのjQuery classNameがこれらの関数のプロパティを使用するためです。

jQueryからの抜粋attributes/classes.js

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

これはHTML要素の場合は期待どおりに動作しますが、SVG要素の場合classNameは少し異なります。SVG要素の場合、classNameは文字列ではなく、のインスタンスですSVGAnimatedString

次のコードを検討してください。

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>

このコードを実行すると、開発者コンソールに次のようなものが表示されます。

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

SVGAnimatedStringjQueryのようにそのオブジェクトを文字列にキャストする[object SVGAnimatedString]と、jQueryが失敗する場所がになります。

jQuery SVGプラグインがこれを処理する方法:

jQuery SVGプラグインは、関連する関数にパッチを適用してSVGサポートを追加することにより、この問題を回避します。

jQuery SVGからの抜粋jquery.svgdom.js

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

この関数は、要素がSVG要素であるかどうかを検出し、そうである場合は、属性にフォールバックする前baseValに、SVGAnimatedStringオブジェクトのプロパティを使用します(使用可能な場合)class

この問題に対するjQueryの歴史的スタンス:

jQueryは現在、この問題をWo n't Fixページにリストしてます。こちらが関連パーツです。

SVG / VMLまたは名前空間要素のバグ

jQueryは主にHTML DOMのライブラリであるため、SVG / VMLドキュメントまたは名前空間要素に関連するほとんどの問題は範囲外です。私たちは、SVGからバブルするイベントなど、HTMLドキュメントに「染み出る」問題に対処しようとします。

明らかに、jQueryは、jQueryコアの範囲外の完全なSVGサポートを検討しており、プラグインにより適しています。


38

あなたが動的クラスを持っているか、どのクラスがすでに適用されているのかわからない場合は、この方法が最善のアプローチだと思います:

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

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

1
物事を成し遂げるために多くのSVG要素を変更する必要があったので、これは私にとっては命の恩人でした。+999私から:)
Karl

2年後、2.xxがリリースされた後でも、jQueryがこの問題を解決できるとは信じがたいです。これらの修正の残りは不必要に複雑でした。ありがとう!
他の追随を許さない創作

17

上記の回答に基づいて、次のAPIを作成しました

/*
 * .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
これは便利ですが、アルゴリズムには問題があります。1.既存のクラス文字列がない場合、クラスを追加すると「undedfined className」が取得されます。2.クラス文字列に正規表現のスーパーセットであるクラスが含まれている場合、クラス文字列にジャンクを残します。たとえば、「dog」クラスを削除しようとして、クラス文字列に「dogfood」が含まれている場合、クラス文字列には「food」が残ります。ここにいくつかの修正があります:jsfiddle.net/hellobrett/c2c2zfto
Brett

1
これは完璧な解決策ではありません。削除するクラスを含むすべての単語を置き換えます。
tom10271

13

ロード後jquery.svg.js、次のファイルをロードする必要があります:http://keith-wood.name/js/jquery.svgdom.js

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

作業例:http : //jsfiddle.net/74RbC/99/


それを追加しました-まだ動作していないようです。私はjquery SVGのサイトのドキュメントを調べてきましたが、その追加機能を利用するために何をする必要があるかについての明確な説明はありません。
Don P

7

不足しているプロトタイプコンストラクターをすべての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);
  }
};

その後、jQueryを必要とせずに次のように使用できます。

this.addClass('clicked');

this.removeClass('clicked');

すべての信用はに行くトッドモト


ie11は、このコードをチョークします。このポリフィルはより良いルートでした:github.com/eligrey/classList.js
ryanrain

5

jQueryはSVG要素のクラスをサポートしていません。あなたは直接の要素を取得することができます$(el).get(0)し、使用classListしてadd / remove。一番上のSVG要素が実際には通常のDOMオブジェクトであり、jQueryの他のすべての要素と同じように使用できるという点でも、これにはトリックがあります。私のプロジェクトでは、必要なものを処理するためにこれを作成しましたが、Mozilla Developer Networkで提供されているドキュメントには、代替として使用できるシムがあります。

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

代わりの方法として、D3.jsをセレクターエンジンとして使用することもできます。私のプロジェクトにはそれを使用して作成されたグラフがあるので、それも私のアプリスコープにあります。D3は、バニラDOM要素とSVG要素のクラス属性を正しく変更します。ただし、この場合にのみD3を追加すると、やり過ぎになります。

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

1
そのd3ビットをありがとう...私は実際にページにすでにd3があったので、それを使用することにしました。非常に便利です:)
Muers

5

jQuery 2.2はSVGクラス操作をサポートします

jQueryの2.2および1.12リリース投稿は、以下の引用が含まれています。

jQueryはHTMLライブラリですが、SVG要素のクラスサポートが役立つことに同意しました。ユーザーが呼び出すことができるようになります.addClass() .removeClass() .toggleClass() 、および.hasClass() SVGのメソッドを。jQueryのは、、class属性ではなくclassNameプロパティを変更します。これにより、クラスメソッドが一般的なXMLドキュメントで使用できるようになります。他の多くのものがSVGでは機能しないことに注意してください。クラスの操作以外に必要な場合は、SVG専用のライブラリを使用することをお勧めします。

jQuery 2.2.0の使用例

それはテストします:

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

その小さな四角をクリックすると、class属性が追加/削除されるので色が変わります。

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

これは、次の問題(依存関係なし)を処理する、かなりエレガントではありますが機能するコードです。

  • classList<svg>IEの要素に存在しない
  • classNameIEの要素のclass属性を表していない<svg>
  • 古いIEの破損getAttribute()setAttribute()実装

classList可能な限り使用します。

コード:

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

使用例:

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

IE7を使用してテストしましたか?LTE IE7 では、CLASS属性を設定、取得、または削除するときにstrAttributeNameが "className"である必要があるためです
Knu

@Knu:通常のHTML要素に対してIE 6および7で確実に機能しclassNameます。これらのブラウザーでは、属性を設定するのではなく、プロパティを使用するためです。「LTE IE7がCLASS属性を設定、取得、または削除するときにstrAttributeNameが「className」である必要がある」という理由は、これらのブラウザーの実装が壊れてgetAttribute(x)おりsetAttribute(x)、単にname xでプロパティを取得または設定するため、より簡単で互換性があるためです。プロパティを直接設定します。
Tim Down

@Knu:OK。IE 7ではSVGがサポートされていないため、確信が持てませんでした<svg>。そのため、IE 7で動作するように設計されたページに要素を含めることによって達成したいことはわかりません。<svg>このような要素は他のカスタム要素と同様に動作するため、IE 7の要素にクラス属性を正常に追加します。
Tim Down

リマインダーへの感謝の気持ちを完全に忘れていました。Adobe SVG Viewerを使って、意図したとおりに機能するかどうかを確認します。
Knu

2

Snap.svgを使用してクラスをSVGに追加します。

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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


1
このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。
トリスタン

1

回避策の1つは、svg要素のコンテナーにaddClassを使用することです。

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

私はこれを自分のプロジェクトで書きました、そしてそれはうまくいきます...おそらく;)

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

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

0

上記の回答、特にSagar Galaに触発されて、私はこのAPIを作成しました。jqueryのバージョンをアップグレードしたくない場合やアップグレードできない場合に使用できます。


-1

または、JQがどこかに真ん中に猿を持っている場合は、昔ながらのDOMメソッドを使用します。

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の人々を学びましょう。2016年のframework-paloozaでさえ、非常に定期的に役立ちます。また、誰かがDOMとアセンブリを比較するのを聞いた場合は、私のためにそれらを蹴ってください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.