jQuery SVG, чому я не можу додати клас?


289

Я використовую 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/…
Кріс,

8
Тож власне питання ЧОМУ я не можу використовувати функції класу jQuery * залишається без відповіді ... Якщо я можу отримати доступ до властивостей елементів SVG за допомогою функції jQuery attr, чому не можуть це зробити і функції класу *? jQuery FAIL?!?
Райан Гріггс

2
Серйозно, хтось знає ЧОМУ ??
Бен Уайльд

Крихітна бібліотека, яка додає цю функціональність для файлів SVG: github.com/toddmotto/lunar
Чак Ле Бутт

6
"Чому", тому що jQuery використовує властивість "className", що є властивістю рядка для елементів HTML, але є SVG-анімованим рядком для елементів SVG. Який не може бути призначений як для елементів HTML. Таким чином, код jQuery вибухає, намагаючись призначити значення.
Джон Ватт

Відповіді:


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
helloworld

досконалий. це має бути відповіддю. хоча РЕАЛЬНА відповідь полягає в тому, щоб jquery включав це за замовчуванням, принаймні як налаштування чи щось таке.
AwokeKnowing

2
.attr("class", "oldclass")(або тим більш громіздким .setAttribute("class", "oldclass")) насправді не рівнозначно видаленню newclass!
Том

Відмінна відповідь (і техніка v.nice) ... але чи було б ідеєю відредагувати питання так, щоб це почалося з заяви, що jquery не може додати клас до SVG (якщо це так ... як здається )?
byronyasgur

1
Просто доповнення: я спробував, і JQuery 3 не вирішив проблему.
Жоао Арруда

76

В API DOM є element.classList, який працює як для елементів HTML, так і для SVG. Немає необхідності у плагіні SVG для jQuery або навіть у jQuery.

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

3
Я перевірив це, і, здається, .classList не визначений для svg-елементів, принаймні в Chrome.
Кріс Джейнес

3
Для мене працює в Chrome. У мене вбудований SVG у HTML, тому моя структура документа виглядає приблизно так: <html> <svg> <круг клас = "jimmy" ...> </svg> </html>
Томаш Мікула

1
Я використовував це для додавання класу в <g> SVG-елемент, прекрасно працює в Chrome.
Rachelle Uy

20
IE не реалізує .classList та API на SVG Elements. Дивіться сторінку caniuse.com/#feat=classlist та список "відомих проблем". Тут також згадуються браузери WebKit.
Шеньме

9
І , як час йде, ця відповідь стає ще більш правильним (і краща відповідь)
Gerrat

69

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 наразі перераховує цю проблему на їхній сторінці Won't Fix . Ось відповідні частини.

Помилки елементів SVG / VML або елементів з простором імен

jQuery - це перш за все бібліотека для HTML DOM, тому більшість проблем, пов’язаних із SVG / VML-документами або елементами, розміщеними в іменах, виходять за межі сфери. Ми намагаємося вирішити проблеми, які "кровоточать" у документах HTML, таких як події, які випливають із SVG.

Очевидно, jQuery вважає повну підтримку SVG поза межами ядра jQuery і краще підходить для плагінів.


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 від мене :)
Карл

Важко повірити в те, що jQuery все ще буде підкопувати свої підбори, навіть через 2 роки та випуск 2.xx. Твій виправлення, нав, врятував мені день. Решта цих виправлень були надмірно складними. Дякую!
неперевершенізначення

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. Якщо немає існуючого рядка класу, після додавання класу ви отримуєте 'не визначений className'. 2. Якщо рядок класу містить клас, який є набором регулярного виразу, ви залишаєте позаду небажану рядок класу. Наприклад, якщо ви спробуєте видалити клас 'dog', а рядок класу містить 'dogfood', у рядку класу вам залишиться 'food'. Ось деякі виправлення: jsfiddle.net/hellobrett/c2c2zfto
Brett

1
Це не ідеальне рішення. Він замінює всі слова, що містять клас, який потрібно видалити.
tom10271

13

Після завантаження jquery.svg.jsви повинні завантажити цей файл: http://keith-wood.name/js/jquery.svgdom.js.

Джерело: http://keith-wood.name/svg.html#dom

Приклад роботи: http://jsfiddle.net/74RbC/99/


Додано - все ще, здається, не працює. Я переглядав документацію на веб-сайті jquery SVG, але немає чіткого пояснення, що потрібно зробити, щоб скористатися її додатковою функціональністю.
День П

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

Весь кредит припадає на Тодд Мото .


тобто11 задихається від цього коду. цей поліфайл був кращим маршрутом: github.com/eligrey/classList.js
ryanrain

5

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 правильно змінює атрибути класу елементів 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 може бути корисною. Тепер користувачі зможуть викликати у SVG методи .addClass () , .removeClass () , .toggleClass () та .hasClass () . 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>


3

Ось мій досить неелегантний, але робочий код, який стосується наступних питань (без будь-яких залежностей):

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

Ви протестували це за допомогою IE7? Оскільки LTE IE7 вимагає, щоб strAttributeName був "className" під час встановлення, отримання або видалення атрибута CLASS .
Кну

@Knu: Це безумовно працює в IE 6 і 7 для звичайних елементів HTML, оскільки в цих браузерах він використовує classNameвластивість, а не намагається встановити атрибут. Причиною того, що "LTE IE7 вимагає, щоб strAttributeName був" className "під час встановлення, отримання або видалення атрибута CLASS", полягає в тому, що ці браузери мають зламану реалізацію getAttribute(x)і setAttribute(x)просто отримують або встановлюють властивість з ім'ям x, так що це простіше і сумісніше щоб встановити властивість безпосередньо.
Тім Даун

@Knu: Гаразд. Я не був впевнений, оскільки SVG не підтримується в IE 7, тому я не впевнений, чого ви сподіваєтеся досягти, включивши <svg>елемент на сторінку, яка призначена для роботи в IE 7. Для чого це варто, мій код дійсно успішно додає атрибут класу до <svg>елементів у IE 7, оскільки такий елемент поводиться як і будь-який інший спеціальний елемент.
Тім Даун

Повністю забув про це спасибі за нагадування. Намагаюся взяти свої руки на Adobe SVG Viewer, щоб перевірити, чи працює він за призначенням.
Кну

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

Або просто використовувати старошкільні методи 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-го палуза це допомагає досить регулярно. Крім того, якщо ви коли-небудь чуєте, щоб хтось порівнював DOM зі збіркою, беріть їх за мене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.