web-dev-qa-db-ja.com

jQuery SVG、なぜclassを追加できないのですか?

私は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がうまく連携していることを私は知っています。なぜなら、私はcanオブジェクトをターゲットにし、クリックされると警告を発するからです。

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

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");
417
forresto

DOM APIには element.classList があり、これはHTMLとSVGの両方の要素に対して機能します。 jQuery SVGプラグインやjQueryさえも必要ありません。

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

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

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

sVGクラス操作を追加します( #219920aaed

この問題に対する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"}

そのSVGAnimatedStringオブジェクトをjQueryのように文字列にキャストすると、[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は現在、この問題を 修正しない ページにリストしています。関連部分です。

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

jQueryは主にHTML DOM用のライブラリなので、SVG/VML文書または名前空間付き要素に関連するほとんどの問題は範囲外です。 SVGから吹き出されるイベントなど、HTMLドキュメントに「流れ出る」問題に対処しようとしています。

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

69

動的クラスがある場合、またはどのクラスがすでに適用されているかわからない場合は、この方法が最善の方法であると考えます。

// 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

上記の回答に基づいて、私は以下の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;
};
16
Sagar Gala

jquery.svg.jsをロードした後、あなたはこのファイルをロードしなければなりません:http://keith-wood.name/js/jquery.svgdom.js

ソース: http://keith-wood.name/svg.html#dom

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

13
bennedich

足りないプロトタイプコンストラクタをすべての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');

すべてのクレジットは Todd Moto になります。

7
Ziad

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

jQuery 2.2 and 1.12 Released 投稿には以下の引用が含まれています。

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

jQuery 2.2. を使った例

それはテストします:

  • 。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>
5

jQueryはSVG要素のクラスをサポートしません。要素を直接$(el).get(0)と取得し、classListadd / 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は、Vanilla DOM要素とSVG要素のクラス属性を正しく修正します。この場合だけD3を追加するのはやり過ぎです。

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

これは私のやや洗練されていないが実用的なコードで、以下の問題を扱います(依存関係はありません)。

  • IEの<svg>要素にclassListが存在しません
  • IEの<svg>要素のclassName属性がclass属性を表していません
  • 旧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");
3
Tim Down

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

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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

2
cjohndesign

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
claytronicon

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

0
Stonecrusher

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

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