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

我使用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.');
});
128452 次浏览

加载jquery.svg.js之后,你必须加载这个文件:http://keith-wood.name/js/jquery.svgdom.js

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

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

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

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

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

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

example .

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

如果你有动态类或者不知道什么类可以被应用,那么我认为这个方法是最好的方法:

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


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

基于以上答案,我创建了以下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;
};

只需将缺失的原型构造函数添加到所有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');

所有功劳归于托德摩托

这里是我的相当不优雅,但工作的代码,处理以下问题(没有任何依赖):

  • IE中的<svg>元素上不存在classList
  • className不代表IE中<svg>元素上的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");

jQuery 3没有这个问题

jQuery 3.0版本中列出的一个变化是:

添加SVG类操作(# 219920 aaed3)

这个问题的一个解决方案是升级到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元素的原因是jQuery 3.0之前的版本对这些函数使用了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元素,如果是,它将使用SVGAnimatedString对象的baseVal属性(如果可用),然后返回到class属性。

jQuery在这个问题上的历史立场:

jQuery目前在无法解决页面上列出了这个问题。以下是相关部分。

SVG/VML或命名空间元素bug

jQuery主要是HTML DOM的库,因此大多数与SVG/VML文档或名称空间元素相关的问题都不在讨论范围之内。我们确实试图解决“流血”的问题;到HTML文档,比如从SVG冒出来的事件。

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

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

var jimmy = Snap(" .jimmy ")


jimmy.addClass("exampleClass");

< a href = " http://snapsvg.io/docs/元素。addClass rel =“nofollow”> http://snapsvg.io/docs/ Element.addClass < / >

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>

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

一个解决方法是将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>

或者,当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与汇编进行比较,请替我踢他们一脚。

我在我的项目中写了这个,它很有效……可能,)

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