jQuery SVG, 왜 클래스를 추가 할 수 없습니까?


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

8
따라서 WHY 의 실제 질문은 jQuery * class 함수를 사용할 수 없습니다. 응답이 없습니다 ... jQuery attr 함수를 통해 SVG 요소의 속성에 액세스 할 수 있다면 왜 * class 함수도이를 수행 할 수 없습니까? jQuery 실패?!?
Ryan Griggs

2
진심으로, 누구나 알고 있습니까 ??
Ben Wilde

SVG 파일에이 기능을 추가하는 작은 라이브러리 : github.com/toddmotto/lunar
Chuck Le Butt

6
"왜"는 jQuery가 "className"속성을 사용하기 때문입니다.이 속성은 HTML 요소의 문자열 속성이지만 SVG 요소의 SVG 애니메이션 문자열입니다. HTML 요소에 좋아요를 할당 할 수 없습니다. 따라서 jQuery 코드는 값을 할당하려고 시도합니다.
Jon Watte

답변:


426

2016 수정 : 다음 두 가지 답변을 읽으십시오.

  • JQuery 3은 근본적인 문제를 해결
  • 바닐라 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 제안을 지원하기 위해- 토론 코드 .blogspot.in
helloworld

완전한. 이것이 답이되어야합니다. REAL 답변은 jquery가 적어도 기본적으로 설정 또는 무언가로 이것을 포함시키는 것입니다.
AwokeKnowing

2
.attr("class", "oldclass")(또는 더 성가신 .setAttribute("class", "oldclass")) 제거하는 것과 실제로 동일하지 않습니다 newclass!
Tom

훌륭한 대답 (및 v.nice 기법) ...하지만 jquery가 SVG에 클래스를 추가 할 수 없다는 것을 시작으로 질문을 편집하는 것이 좋습니다 (그렇다면 ... )?
byronyasgur

1
부록 : JQuery 3에서 문제를 해결하지 못했습니다.
Joao Arruda

76

DOM API에는 HTML 및 SVG 요소 모두에 대해 작동하는 element.classList 가 있습니다 . jQuery SVG 플러그인이나 jQuery가 필요하지 않습니다.

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

3
나는 이것을 테스트했고 적어도 Chrome에서는 svg 하위 요소에 대해 .classList가 정의되지 않은 것으로 보입니다.
Chris Jaynes

3
Chrome에서 작동합니다. 나는 SVG, HTML에 포함 된 때문에,이 같은 내 문서 구조 외모 : <HTML> <SVG> <원 클래스 = "지미"...> </ SVG> </ HTML>
토마스 Mikula

1
이것을 사용하여 <g> SVG 요소에 클래스를 추가하고 Chrome에서 제대로 작동합니다.
Rachelle Uy

20
IE는 SVG Elements에서 .classList 및 API를 구현하지 않습니다. caniuse.com/#feat=classlist 및 "알려진 문제"목록을 참조하십시오 . WebKit 브라우저도 언급됩니다.
Shenme

9
그리고 시간이 지남에
따라이

69

jQuery 3에는이 문제가 없습니다

jQuery 3.0 개정판 에 나열된 변경 사항 중 하나 는 다음과 같습니다.

SVG 클래스 조작 추가 ( # 2199 , 20aaed3 )

이 문제에 대한 한 가지 해결책은 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 요소인지 여부를 감지하고, 가능한 경우 해당 요소가 SVG 요소로 넘어 가기 전에 객체 의 baseVal속성을 사용합니다 SVGAnimatedString.class 합니다.

문제에 대한 jQuery의 역사적 입장 :

jQuery는 현재이 문제를 수정 안 함 페이지 에 나열 합니다. 관련 부품은 다음과 같습니다.

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 년이 지난 후에도 jQuery가 여전히 문제를 겪고 있다고 생각하기는 어렵습니다. 2.xx 릴리스 수정 사항, 탐색은 하루를 저축했습니다. 이 수정의 나머지는 불필요하게 복잡했습니다. 감사!
비교할 수없는

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. 기존 클래스 문자열이없는 경우 클래스를 추가 할 때 '정의되지 않은 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');

모든 크레딧은 Todd Moto 로갑니다 .


ie11이이 코드를 질식시킵니다. 이 polyfill은 더 나은 경로였습니다 : github.com/eligrey/classList.js
ryanrain

5

jQuery는 SVG 요소 클래스를 지원하지 않습니다. 당신은 요소를 직접 얻을 수 있습니다 $(el).get(0)및 사용 classListadd / remove. 최상위 SVG 요소가 실제로는 일반적인 DOM 객체이며 jQuery의 다른 모든 요소와 같이 사용할 수 있다는 점에서 트릭이 있습니다. 내 프로젝트에서 필자가 필요한 것을 처리하기 위해 이것을 만들었지 만 Mozilla Developer Network 에 제공된 설명서 에는 대안으로 사용할 수있는 shim이 있습니다.

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는 이제 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 : IE 6 및 7에서 일반 HTML 요소에 대해 올바르게 작동합니다. className 속성을 설정하지 않고 속성을 사용하기 때문입니다. "설정, 점점, 또는 CLASS 속성을 제거 할 때"그 브라우저의 깨진 구현해야한다는 것입니다 클래스 이름 "LTE IE7이 될 strAttributeName을 요구"하는 이유 getAttribute(x)setAttribute(x)이름을 단순히 가져 오거나 설정할 속성을 x보다 쉽고 호환 그래서, 속성을 직접 설정합니다.
Tim Down

@Knu : 알겠습니다. IE 7에서 SVG가 지원되지 않기 때문에 확실하지 않으므로 IE 7에서 <svg>작동하도록 설계된 페이지에 요소를 포함 시켜서 달성하고자하는 것이 무엇인지 확실하지 않습니다 . <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
이 링크가 질문에 대한 답변을 제공 할 수 있지만 여기에 답변의 필수 부분을 포함시키고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않을 수 있습니다.
Tristan

1

한 가지 해결 방법은 svg 요소의 컨테이너에 Class를 추가하는 것입니다.

$('.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 년 프레임 워크 팔로 자에서도 꽤 정기적으로 도움이됩니다. 또한 누군가가 DOM을 어셈블리와 비교하는 것을 듣는다면 저를 위해 차십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.