295

我正在使用 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

15 回答 15

433

编辑 2016:阅读接下来的两个答案。

  • 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");
于 2012-04-21T09:27:21.300 回答
76

DOM API 中有element.classList可用于 HTML 和 SVG 元素。不需要 jQuery SVG 插件甚至 jQuery。

$(".jimmy").click(function() {
    this.classList.add("clicked");
});
于 2013-04-04T21:06:45.120 回答
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"}

如果我们像 jQuery 那样将该SVGAnimatedString对象转换为字符串,我们就会有[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 目前在他们的Won't Fix页面上列出了这个问题。这是相关部分。

SVG/VML 或命名空间元素错误

jQuery 主要是一个用于 HTML DOM 的库,因此大多数与 SVG/VML 文档或命名空间元素相关的问题都超出了范围。我们确实尝试解决“渗透”到 HTML 文档的问题,例如从 SVG 冒出的事件。

显然 jQuery 认为完全 SVG 支持超出了 jQuery 核心的范围,并且更适合插件。

于 2015-04-08T21:32:14.443 回答
38

如果您有动态类或不知道可以应用哪些类,那么我认为这种方法是最好的方法:

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

// removeClass
$('path').attr('class', function(index, classNames) {
    return classNames.replace('class-name', '');
});
于 2013-11-26T15:31:28.313 回答
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;
};
于 2014-06-12T22:28:00.430 回答
13

加载后jquery.svg.js,您必须加载此文件:http://keith-wood.name/js/jquery.svgdom.js.

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

工作示例:http: //jsfiddle.net/74RbC/99/

于 2011-12-27T00:18:38.697 回答
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');

所有功劳归功于托德·摩托

于 2014-11-19T19:56:02.297 回答
5

jQuery 不支持 SVG 元素的类。您可以直接获取元素$(el).get(0)并使用classListand add / remove。这也有一个技巧,最顶层的 SVG 元素实际上是一个普通的 DOM 对象,并且可以像 jQuery 中的所有其他元素一样使用。在我的项目中,我创建了这个来处理我需要的东西,但是Mozilla 开发者网络上提供的文档有一个 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 正确修改了 vanilla DOM 元素和 SVG 元素的类属性。尽管仅针对这种情况添加 D3 可能有点过分。

d3.select(el).classed('myclass', true);
于 2013-07-23T15:51:21.103 回答
5

jQuery 2.2 支持 SVG 类操作

jQuery 2.2 和 1.12 发布的帖子包括以下引用:

虽然 jQuery 是一个 HTML 库,但我们同意对 SVG 元素的类支持可能很有用。用户现在可以在 SVG 上调用.addClass().removeClass().toggleClass().hasClass()方法。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>

于 2016-01-09T19:35:23.647 回答
4

我使用 Snap.svg 向 SVG 添加一个类。

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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

于 2015-12-07T21:16:43.633 回答
3

这是我处理以下问题的相当不雅但有效的代码(没有任何依赖关系):

  • classList不存在<svg>于 IE 中的元素上
  • className不代表IEclass中元素的属性<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");
于 2015-02-11T11:55:08.873 回答
1

一种解决方法可能是将类添加到 svg 元素的容器中:

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

于 2016-08-20T14:21:42.727 回答
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')
于 2017-09-01T11:09:56.783 回答
0

受上述答案的启发,尤其是受到 Sagar Gala 的启发,我创建了这个API。如果您不想或无法升级您的 jquery 版本,您可以使用它。

于 2016-07-11T12:12:17.410 回答
-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 与程序集进行比较,请替我踢他们。

于 2016-12-19T23:44:16.043 回答