jQuery SVG,为什么我不能添加类?


288

我正在使用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
因此,为什么我不能使用jQuery * class函数的实际问题仍未得到解决...如果我可以通过jQuery attr函数访问SVG元素的属性,为什么* class函数也不能这样做?jQuery失败?!?
Ryan Griggs 2014年

2
说真的,有人知道为什么吗?
本·王尔德

微小的库,为SVG文件添加了此功能:github.com/toddmotto/lunar
Chuck Le Butt

6
“为什么”是因为jQuery使用“ className”属性,该属性是HTML元素的字符串属性,但是是SVG元素的SVG动画字符串。对于HTML元素,无法将其分配给like。因此,jQuery代码会爆炸,试图分配值。
乔恩·瓦特

Answers:


426

编辑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");

在以forresto建议表示支持- discusscode.blogspot.in/2012/08/...
的HelloWorld

完善。这应该是答案。尽管REAL答案是让jquery默认包含此设置,至少作为设置或其他内容。
AwokeKnowing

2
.attr("class", "oldclass")(或更麻烦.setAttribute("class", "oldclass"))实际上并不等同于删除newclass
汤姆

很好的答案(和v.nice技术)...但是编辑问题以使它首先声明jquery无法向SVG添加类(如果是这样的话... )?
byronyasgur 2014年

1
只是一个附录:我尝试过,而JQuery 3并没有解决问题。
Joao Arruda'9

76

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

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

3
我对此进行了测试,至少在Chrome中,.svg子元素的.classList似乎未定义。
克里斯·杰恩斯

3
在Chrome中为我工作。我将SVG嵌入HTML中,因此我的文档结构如下所示:<html> <svg> <circle class =“ jimmy” ...> </ svg> </ html>
Tomas Mikula

1
我用它在<g> SVG元素中添加类,在Chrome中工作正常。
Rachelle Uy

20
IE在SVG Elements上未实现.classList和API。请参阅caniuse.com/#feat=classlist和“已知问题”列表。那也提到了WebKit浏览器。
Shenme 2014年

9
随着时间的流逝,这个答案变得更加正确(也是最好的答案)
Gerrat 2015年

69

jQuery 3没有这个问题

jQuery 3.0修订版中列出的更改之一是:

添加SVG类的操作(#219920aaed3

解决此问题的一种方法是升级到jQuery3。它的效果很好:

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文档或命名空间元素有关的问题不在范围之内。我们确实试图解决“渗入” HTML文档的问题,例如从SVG冒出来的事件。

显然,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 from me :)
卡尔

很难相信jQuery即使在2年后以及2.xx版本发布后仍然会为此而努力,您的修复程序nav为我节省了时间。这些修复程序的其余部分不必要地复杂。谢谢!
无与伦比的

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.如果没有现有的类字符串,则在添加类时会得到“未定义的类名”。2.如果类字符串包含作为正则表达式超集的类,则您将在类字符串中留下垃圾。例如,如果您尝试删除“狗”类,并且类字符串包含“ dogfood”,则在类字符串中将剩下“食物”。这里有一些修复:jsfiddle.net/hellobrett/c2c2zfto
Brett

1
这不是一个完美的解决方案。它替换了包含要删除的类的所有单词。
tom10271 2015年


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
ryanrain

5

jQuery不支持SVG元素类。您可以直接获取元素$(el).get(0)并使用classListadd / remove。这样做还有一个窍门,就是最上面的SVG元素实际上是一个普通的DOM对象,可以像jQuery中的所有其他元素一样使用。在我的项目中,我创建了它来照顾我需要的东西,但是Mozilla开发人员网络上提供的文档中有一个垫片,可以用作替代品。

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中不存在的元素
  • className不代表IE中元素的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”
2015年

@Knu:对于常规HTML元素,它绝对适用于IE 6和7,因为在那些浏览器中,它使用className属性而不是尝试设置属性。“ LTE IE7在设置,获取或删除CLASS属性时要求strAttributeName为“ className”的原因是,这些浏览器的实现方式很差,getAttribute(x)并且setAttribute(x)只是使用name来获取或设置属性x,因此更加容易且兼容直接设置属性。
蒂姆·唐

@Knu:好的。我不确定,因为IE 7不支持SVG,所以我不确定要<svg>在IE 7中设计的页面中包含一个元素来实现什么目标。确实向<svg>IE 7中的元素成功添加了class属性,因为这样的元素的行为类似于任何其他自定义元素。
蒂姆·唐

完全忘记了,谢谢您的提醒。我会尝试使用Adobe SVG Viewer来检查它是否按预期工作。
2015年

2

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

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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


1
尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会无效。
特里斯坦,

1

一种解决方法是将class添加到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>


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与程序集进行比较,请为我踢。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.