我正在使用 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.');
});
编辑 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 3 没有这个问题
the jQuery 3.0 revisions 中列出的更改之一是:
添加 SVG 类操作 (#2199, 20aaed3)
这个问题的一个解决方案是升级到 jQuery 3。它工作得很好:
var翻转=真; setInterval(function() { // 可以使用 toggleClass,但演示 addClass. if (flip) { $('svg circle').addClass('green'); } else { $('svg circle').removeClass('绿色'); } 翻转 = !flip; }, 1000); svg 圆 { 填充:红色;中风:黑色;笔画宽度:5; } svg circle.green { 填充:绿色; }
问题:
jQuery 类操作函数不适用于 SVG 元素的原因是 3.0 之前的 jQuery 版本对这些函数使用 className
属性。
jQuery 属性/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 { 宽度:200px;高度:50px;背景:蓝色; }
如果您运行此代码,您将在开发者控制台中看到类似以下内容。
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 目前在他们的 Won’t Fix 页面上列出了这个问题。这是相关部分。
SVG/VML 或命名空间元素错误 jQuery 主要是用于 HTML DOM 的库,因此与 SVG/VML 文档或命名空间元素相关的大多数问题都超出了范围。我们确实尝试解决“渗透”到 HTML 文档的问题,例如从 SVG 冒出的事件。
显然 jQuery 认为完全 SVG 支持超出了 jQuery 核心的范围,并且更适合插件。
如果您有动态类或不知道可以应用哪些类,那么我认为这种方法是最好的方法:
// 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;
};
加载 jquery.svg.js
后,您必须加载此文件:http://keith-wood.name/js/jquery.svgdom.js
。
来源:http://keith-wood.name/svg.html#dom
工作示例:http://jsfiddle.net/74RbC/99/
只需将缺少的原型构造函数添加到所有 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。
jQuery 不支持 SVG 元素的类。您可以直接获取元素 $(el).get(0)
并使用 classList
和 add / 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);
jQuery 2.2 支持 SVG 类操作
jQuery 2.2 and 1.12 Released 帖子包含以下引用:
虽然 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 { 填充:红色!重要; }
我使用 Snap.svg 向 SVG 添加一个类。
var jimmy = Snap(" .jimmy ")
jimmy.addClass("exampleClass");
http://snapsvg.io/docs/#Element.addClass
这是我处理以下问题的相当不雅但有效的代码(没有任何依赖关系):
IE 中的
className 不代表 IE 中
旧 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");
className
属性而不是尝试设置属性。 “在设置、获取或删除 CLASS 属性时,LTE IE7 要求 strAttributeName 为“className””的原因是那些浏览器的 getAttribute(x)
和 setAttribute(x)
实现有缺陷,它们只是获取或设置名称为 { 4},因此直接设置属性更容易,更兼容。
<svg>
元素来实现什么。对于它的价值,我的代码确实向 IE 7 中的 <svg>
元素成功添加了一个类属性,因为这样的元素的行为与任何其他自定义元素一样。
一种解决方法可能是将类添加到 svg 元素的容器中:
$('.svg-container').addClass('svg-red'); .svg-red svg circle{ 填充:#ED3F32; }
我在我的项目中写了这个,它可以工作......可能;)
$.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')
或者,当 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 与程序集进行比较,请替我踢他们。
不定期副业成功案例分享
.attr("class", "oldclass")
(或更繁琐的.setAttribute("class", "oldclass")
)真的不等于去掉newclass
!