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.');
});

person Don P    schedule 26.12.2011    source источник
comment
Хорошая статья здесь: toddmotto.com/   -  person Kris    schedule 12.06.2014
comment
Таким образом, фактический вопрос ПОЧЕМУ я не могу использовать функции класса * jQuery, остается без ответа... Если я могу получить доступ к свойствам элементов SVG через функцию атрибута jQuery, почему не могут функции * класса также сделать это? jquery не удалось?!?   -  person Ryan Griggs    schedule 02.12.2014
comment
Серьезно, кто-нибудь знает ПОЧЕМУ??   -  person Ben Wilde    schedule 20.02.2015
comment
Крошечная библиотека, которая добавляет эту функциональность для файлов SVG: github.com/toddmotto/lunar   -  person Chuck Le Butt    schedule 08.04.2015
comment
Причина в том, что jQuery использует свойство className, которое является строковым свойством для элементов HTML, но представляет собой анимированную строку SVG для элементов SVG. Который не может быть назначен лайком для HTML-элементов. Таким образом, код jQuery взрывается, пытаясь присвоить значение.   -  person Jon Watte    schedule 20.05.2016
comment
Согласно stackoverflow.com/a/34698009/70345 правильный ответ: обновить до одного из jQuery 1.12.0, 2.2 0,0 или 3,0; или более поздняя версия этих.   -  person Ian Kemp    schedule 26.09.2017


Ответы (15)


Изменить 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");
person forresto    schedule 21.04.2012
comment
В поддержку предложения forresto — discusscode.blogspot. в/2012/08/ - person helloworld; 05.08.2012
comment
идеально. это должен быть ответ. хотя РЕАЛЬНЫЙ ответ заключается в том, чтобы jquery включал это по умолчанию, по крайней мере, как настройку или что-то в этом роде. - person AwokeKnowing; 05.11.2013
comment
.attr("class", "oldclass") (или более громоздкий .setAttribute("class", "oldclass")) на самом деле не эквивалентен удалению newclass! - person Tom; 29.07.2014
comment
Отличный ответ (и техника v.nice)... но было бы неплохо отредактировать вопрос так, чтобы он начинался с утверждения, что jquery не может добавить класс в SVG (если это так... как кажется )? - person byronyasgur; 16.12.2014
comment
Версия без jQuery не будет работать в старом IE (конечно, 7 и более ранних версиях, не помню насчет 8), потому что setAttribute() не работает. - person Tim Down; 11.02.2015
comment
Сагар Гала опубликовал гораздо лучшее решение. Установка класса вручную отличается от функции jquery add/removeClass. - person Oleksandr IY; 10.04.2015
comment
ОП спросил почему, а не как. так что этот ответ вообще не ответил на вопрос. - person vsync; 21.08.2015
comment
Кто-нибудь знает, как сохранить существующие классы, потому что они стираются, когда я добавляю класс атрибутов? - person finners; 22.03.2016
comment
@FrazerFindlater oldclass в примере для этого $("#item").attr("class", "oldclass newclass"); - person forresto; 29.03.2016
comment
Просто дополнение: я пробовал, и JQuery 3 не решил проблему. - person Joao Arruda; 08.09.2016
comment
Что касается вашего редактирования classList 2016 года, пожалуйста, прочитайте комментарий в ответе Томаса Микулы ниже. IE не реализует classList в элементах SVG. - person TimHayes; 04.01.2017
comment
небольшой обходной путь, с которым я столкнулся: вместо $("#item").addClass("newClass") я пишу $("#item").attr("class", $(#item).attr('class').replace('newClass','')+' newClass');, а для $("#item").removeClass("oldClass"); я пишу $("#item").attr("class", $(#item).attr('class').replace('oldClass','')); таким образом, у вас нет дубликатов класса - person Fenix Aoras; 12.04.2017
comment
Спасибо! Использовал это, чтобы добавить класс к маркерам SVG листовки: L.circle([lat, lng], radius)._path.classList.add("circlemarkerclassname") - person nothingisnecessary; 28.11.2017

В API DOM есть element.classList, который работает как для HTML- и SVG-элементы. Нет необходимости в плагине jQuery SVG или даже jQuery.

$(".jimmy").click(function() {
    this.classList.add("clicked");
});
person Tomas Mikula    schedule 04.04.2013
comment
Я проверил это, и .classList кажется неопределенным для подэлементов svg, по крайней мере, в Chrome. - person Chris Jaynes; 25.04.2013
comment
У меня работает в Хроме. У меня SVG встроен в HTML, поэтому структура моего документа выглядит так: ‹html›‹svg›‹circle class=jimmy ...›‹/svg›‹/html› - person Tomas Mikula; 28.05.2013
comment
Я использовал это, чтобы добавить класс в элемент SVG ‹g›, отлично работает в Chrome. - person Rachelle Uy; 14.08.2013
comment
IE не реализует .classList и API для элементов SVG. См. caniuse.com/#feat=classlist и список известных проблем. Это также касается браузеров WebKit. - person Shenme; 13.02.2014
comment
Кажется, довольно хрупкий подход. - person James; 20.06.2014
comment
@Shenme В списке известных проблем об этом нет упоминания. - person Chuck Le Butt; 08.04.2015
comment
И со временем этот ответ становится еще более правильным (и лучшим ответом) - person Gerrat; 10.04.2015
comment
@Chuck: в примечаниях Caniuse, относящихся к IE10,11: частичная поддержка означает отсутствие работы с элементами SVG. - person ptim; 14.05.2015
comment
Он работает в (всех современных) Chrome, но не в IE для svg. 10+ IE поддерживает classList, но не изменяет элементы svg. См. раздел CanIUseThis. - person Tristan Tao; 25.01.2016
comment
Здесь есть полифилл для старых браузеров developer.mozilla.org/ en-US/docs/Web/API/Element/classList - person Pete Thorne; 03.05.2016

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, заключается в том, что версии 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, если оно доступно, прежде чем вернуться к атрибуту class.

Историческая позиция jQuery по этому вопросу:

В настоящее время jQuery перечисляет эту проблему на своей странице Не исправить. Вот соответствующие части.

Ошибки SVG/VML или элементов с пространством имен

jQuery — это в первую очередь библиотека для HTML DOM, поэтому большинство проблем, связанных с документами SVG/VML или элементами пространства имен, выходят за рамки. Мы пытаемся решить проблемы, которые проявляются в HTML-документах, например, события, всплывающие из SVG.

Очевидно, jQuery считал, что полная поддержка SVG выходит за рамки ядра jQuery и лучше подходит для плагинов.

person Alexander O'Mara    schedule 08.04.2015

Если у вас есть динамические классы или вы не знаете, какие классы уже могут быть применены, то этот метод, я считаю, лучшим подходом:

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

// removeClass
$('path').attr('class', function(index, classNames) {
    return classNames.replace('class-name', '');
});
person nav    schedule 26.11.2013
comment
Это было для меня спасением, так как мне нужно было изменить множество SVG-элементов, чтобы добиться цели. +999 от меня :) - person Karl; 18.06.2015
comment
Трудно поверить, что jQuery все еще упирается в это, даже спустя 2 года и выпуска 2.x.x. Ваше исправление, нав, спасло мне день. Остальные исправления были излишне сложными. Спасибо! - person unrivaledcreations; 16.08.2015

Основываясь на приведенных выше ответах, я создал следующий 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;
};
person Sagar Gala    schedule 12.06.2014
comment
Это полезно, но у алгоритмов есть проблемы: 1. Если нет существующей строки класса, при добавлении класса вы получаете 'undedfined className'. 2. Если строка класса содержит класс, являющийся надмножеством регулярного выражения, в строке класса остается мусор. Например, если вы попытаетесь удалить класс «собака», а строка класса содержит «собачий корм», в строке класса останется «еда». Вот некоторые исправления: jsfiddle.net/hellobrett/c2c2zfto - person Brett; 15.12.2015
comment
Это не идеальное решение. Он заменяет все слова, содержащие класс, который вы хотите удалить. - person tom10271; 23.12.2015

После загрузки jquery.svg.js вы должны загрузить этот файл: http://keith-wood.name/js/jquery.svgdom.js.

Источник: http://keith-wood.name/svg.html#dom

Рабочий пример: http://jsfiddle.net/74RbC/99/

person bennedich    schedule 27.12.2011
comment
Добавил - все равно не работает. Я просматривал документацию на сайте jquery SVG, но нет четкого объяснения того, что вам нужно сделать, чтобы использовать его дополнительные функции. - person Don P; 27.12.2011

Просто добавьте отсутствующий конструктор прототипа ко всем узлам 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');

Все заслуги принадлежат Тодду Мото.

person Community    schedule 19.11.2014
comment
ie11 задыхается от этого кода. этот полифил был лучшим маршрутом: github.com/eligrey/classList.js - person ryanrain; 31.05.2019

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 правильно изменяет атрибуты класса ванильных элементов DOM и элементов SVG. Хотя добавление D3 только для этого случая, вероятно, было бы излишним.

d3.select(el).classed('myclass', true);
person QueueHammer    schedule 23.07.2013
comment
Спасибо за этот бит d3 ... На самом деле у меня уже был d3 на странице, поэтому я просто решил использовать его. Очень полезно :) - person Muers; 02.12.2014

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>

person Elrond_EGLDer    schedule 09.01.2016

Я использую Snap.svg для добавления класса и SVG.

var jimmy = Snap(" .jimmy ")

jimmy.addClass("exampleClass");

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

person cjohndesign    schedule 07.12.2015
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - person Tristan; 08.12.2015

Вот мой довольно неэлегантный, но работающий код, который решает следующие проблемы (без каких-либо зависимостей):

  • classList не существует в <svg> элементах в IE
  • className не представляет атрибут class для <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");
person Tim Down    schedule 11.02.2015
comment
Вы тестировали его с помощью IE7? Поскольку LTE IE7 требует, чтобы strAttributeName был className при установке, получении или удалении атрибута CLASS. - person Knu; 08.06.2015
comment
@Knu: Это определенно работает в IE 6 и 7 для обычных элементов HTML, потому что в этих браузерах он использует свойство className, а не пытается установить атрибут. Причина, по которой LTE IE7 требует, чтобы strAttributeName было className при установке, получении или удалении атрибута CLASS, заключается в том, что эти браузеры имеют неработающую реализацию getAttribute(x) и setAttribute(x), которые просто получают или устанавливают свойство с именем x, поэтому проще и совместимее установить свойство напрямую. - person Tim Down; 08.06.2015
comment
@Кну: хорошо. Я не был уверен, поскольку SVG не поддерживается в IE 7, поэтому я не уверен, чего вы надеетесь достичь, включив элемент <svg> на страницу, предназначенную для работы в IE 7. Что бы это ни стоило, мой код успешно добавляет атрибут класса к элементам <svg> в IE 7, поскольку такой элемент ведет себя как любой другой пользовательский элемент. - person Tim Down; 09.06.2015
comment
Совсем забыл об этом, спасибо за напоминание. Я попытаюсь получить в свои руки Adobe SVG Viewer, чтобы проверить, работает ли он так, как задумано. - person Knu; 09.06.2015

Одним из обходных путей может быть добавление класса в контейнер элемента 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>

person Claytronicon    schedule 20.08.2016

Я написал это в своем проекте, и это работает... наверное;)

$.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')
person Dariusz Majchrzak    schedule 01.09.2017

Вдохновленный приведенными выше ответами, особенно ответом Сагара Гала, я создал этот API. Вы можете использовать его, если не хотите или не можете обновить версию jquery.

person Stonecrusher    schedule 11.07.2016

Или просто используйте методы 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 году framework-palooza помогает довольно регулярно. Кроме того, если вы когда-нибудь услышите, как кто-то сравнивает DOM с ассемблером, пните его от меня.

person Erik Reppen    schedule 19.12.2016