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 работают вместе, потому что я can цель объект и стрелять предупреждение, когда он нажал:
$(".jimmy").click(function() {
alert('Handler for .click() called.');
});
15 ответов
Edit 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");
здесь элемент.classList в DOM API, который работает как для HTML, так и для SVG-элементов. Нет необходимости в плагине jQuery SVG или даже jQuery.
$(".jimmy").click(function() {
this.classList.add("clicked");
});
jQuery 3 не имеет этой проблемы
одно из изменений, перечисленных на версии jQuery 3.0 - это:
одним из решений этой проблемы было бы обновление до 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"}
если бы мы бросили это 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
объект, если он доступен, прежде чем вернуться на .
историческая позиция jquery's по этому вопросу:
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'), '');
if (this.hasClass(className)) {
this.setAttribute('class', removedClass);
}
};
вы можете использовать его таким образом, не требуя jQuery:
this.addClass('clicked');
this.removeClass('clicked');
все Тодд Мото.
jQuery не поддерживает классы элементов SVG. Вы можете получить элемент $(el).get(0)
и использовать classList
и add / 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 корректно изменяет атрибуты класса элементов vanilla DOM и SVG. Хотя добавление D3 только для этого случая, вероятно, будет излишним.
d3.select(el).classed('myclass', true);
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>
вот мой довольно неэлегантный, но рабочий код, который имеет дело со следующими проблемами (без каких-либо зависимостей):
-
classList
не существующих на<svg>
элементы в IE -
className
не представляющейclass
атрибут on<svg>
элементы в 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");
Я использую Snap.svg для добавления класса и SVG.
var jimmy = Snap(" .jimmy ")
jimmy.addClass("exampleClass");
одним обходным путем может быть добавление класса в контейнер элемента 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>
вдохновленный ответами выше, особенно Сагар Гала, я создал это API. Вы можете использовать его, если не хотите или не можете обновить версию jquery.
Я написал это в своем проекте, и это работает... вероятно;)
$.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')
или просто используйте методы старой школы DOM, когда JQ имеет обезьяну где-то посередине.
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-го-palooza это помогает довольно регулярно. Кроме того, если вы когда-нибудь услышите, как кто-то сравнивает DOM с assembly, пните их для меня.