22

I have a set of SVG elements with the classes node and link. My program should detect whether an element has the node class or the link class upon hovering over any of the SVG elements. However, for some reason, the .hasClass() doesn't seem to work:

$(".node").hover(function(evt){
    console.log($(this).attr("class")); //returns "node"
    console.log($(this).hasClass('node')); //returns false
}, function(){console.log("Done");});

So the element I hovered on has the class node, and jQuery detects that too, as shown by console.log($(this).attr("class"));, but for some reason the actual .hasClass() fails. Why is this? Is it failing because of the SVG?

Shrey Gupta
  • 5,509
  • 8
  • 45
  • 71
  • 1
    just a side note, youre missing `);` in the end :) and, [seems to work for me](http://jsfiddle.net/hungerpain/V4BNT/) – krishwader Jul 24 '13 at 17:36
  • Interesting...try setting a variable equal to `$(this).attr("class")` then use `hasClass` with that var. Still false? – tymeJV Jul 24 '13 at 17:37
  • 5
    Elements inside SVG aren't really HTML, and as such there are properties missing from those elements, that's why getting the attribute works, but using hasClass, which probably checks the className property, does not work. – adeneo Jul 24 '13 at 17:37
  • @hungerpain Ah thank you. I have that in my original code, but when I copied it to SO, I forgot it. – Shrey Gupta Jul 24 '13 at 17:37
  • @adeneo Do you happen to know what I could use? My elements actually have multiple classes, so I cannot simply use `if($(this).attr("class") === "node"){//code here}` to check. – Shrey Gupta Jul 24 '13 at 17:39
  • `$(this).attr("class").indexOf('node') != -1` – adeneo Jul 24 '13 at 17:40
  • 2
    It's not really the same, but as long as none of the other classes contains the string "node" it would work. – adeneo Jul 24 '13 at 17:40
  • @adeneo Thanks! If you write up a formal answer, I would be more than happy to accept it. And that should work for my case. – Shrey Gupta Jul 24 '13 at 17:41
  • I'm sure someone else will, I don't really need the rep, give it to someone else. – adeneo Jul 24 '13 at 17:42
  • 2
    And remember that when working with SVG, the elements don't have stuff like className, innerHTML etc. so not everything in jQuery will work on those elements. – adeneo Jul 24 '13 at 17:43
  • Definitely. I was trying to extract the content of a `` element and spent a good 10 minutes before realizing that `innerHTML` wouldn't work. – Shrey Gupta Jul 24 '13 at 17:44
  • @adeneo: Of course SVG elements should have a [`className` property](http://www.w3.org/TR/SVG/types.html#__svg__SVGStylable__className). – Bergi Jul 24 '13 at 18:21
  • @Bergi - frack, someone finally tested it. They do, and hasClass works just fine! – adeneo Jul 24 '13 at 19:07
  • @Bergi or maybe not so much -> http://jsfiddle.net/W6pwC/ – adeneo Jul 24 '13 at 19:13
  • 2
    **jQuery 2.2.0** fixed this problem - you can see my post here: http://stackoverflow.com/a/34698009/3885376 containing some details + a small example that now `hasClass()`, `addClass()` and `removeClass()` work. – ROMANIA_engineer Jan 09 '16 at 19:43

5 Answers5

11

The class attribute for HTML element doesn't have the same meaning in SVG.

$("<b></b>").addClass($(this).attr("class")).hasClass("node")

Or

/(^|\s)node(\s|$)/.test($(this).attr("class"))

for SVG elements.

EDIT .hasClass seems to work just fine (at least in IE9 and FF) http://jsfiddle.net/X6BPX/1/

So the problem could be any combination of the following: a syntax error, using an outdated browser, using an outdated version of jQuery.

Louis Ricci
  • 20,804
  • 5
  • 48
  • 62
  • What else would `class` mean in SVG? – Bergi Jul 24 '13 at 18:17
  • @Bergi - It's just a regular attribute instead of a special styling attribute like it is in HTML elements with browser specific hooks like className vs class. – Louis Ricci Jul 24 '13 at 18:19
  • @Bergi - http://jsfiddle.net/TRKF6/ notice how elm.class, elm.className, elm.getAttribute('class'), elm.getAttribute('className') all respond differently than a standard HTML Element. Older versions of jQuery may not account for these differences. – Louis Ricci Jul 24 '13 at 18:36
  • Nope, only the `className` property yields a different value. Check http://jsfiddle.net/TRKF6/1/ for a comparison. If you get different values for the other properties you should probably update your browser. – Bergi Jul 24 '13 at 20:08
  • @Bergi - Your right just className is different, and that's enough to break an outdated JavaScript library that expects className to act a specific way; it's also enough to say that SVG and HTML elements are not equivalent with regards to class. – Louis Ricci Jul 24 '13 at 21:13
  • 5
    I'm not sure what the linked (edit) JSFiddle intends to prove, as it's using a div. Here's one with an SVG that fails as the question suggests: http://jsfiddle.net/Mm3TE/ – Ben Mosher Jul 15 '14 at 14:16
7

As Bergi pointed out in comments, jQuery silently fails on SVG elements on account of className returning an SVGAnimatedString object instead of a normal DOMString.

See this JSFiddle for a comparison.

I was tempted to submit a pull request on this, but did a quick project search, and apparently the jQuery project stance on SVG issues is wontfix: https://github.com/jquery/jquery/pull/1511

If you're using D3, you could use d3.select(this).classed('node'). Note that D3 correctly returns for both HTML elements and SVG elements.

Ben Mosher
  • 13,251
  • 7
  • 69
  • 80
  • It would be nice if jQuery's documentation stated this lack of support (or unexpected results). The [hasClass() API doc](http://api.jquery.com/hasClass()/) says _"As of jQuery 1.12/2.2, this method supports XML documents, including SVG"_ – Ricardo Jun 07 '19 at 00:57
4

This is not the fastest option ever, but it is a possible solution. Instead of using jQuery's hasClass you could instead obtain the class attribute as a string and use indexOf to search through it. There are probably use cases where this will fail, so I wouldn't recommend this except for super simple projects.

Working example:

var s = $(this).attr('class');
if( s.indexOf('node')!==-1 ){
    // do something
}

Remember: indexOf returns -1 when it can't find anything, not 0. 0 is returned when the substring starts at index 0.

Muhammad Abdul-Rahim
  • 1,980
  • 19
  • 31
1

This is a hack for addClass, removeClass, hasClass jquery methods for before jquery 3.x.x versions.

$.fn.extend({
    addSVGClass: function (cls) {
        return this.each(function () {
            var classList = $(this).attr('class');
            if (classList) {
                var classListArr = classList.split(" ");
                if (classListArr.indexOf(cls) === -1) {
                    classListArr.push(cls);
                    classList = classListArr.join(" ").trim();
                    $(this).attr('class', classList);
                }
            } else {
                $(this).attr('class', cls);
            }
        });
    },
    removeSVGClass: function (cls) {
        return this.each(function () {
            var classList = $(this).attr('class');
            if (classList) {
                var classListArr = classList.split(" ");
                if (classListArr.indexOf(cls) !== -1) {
                    delete classListArr[classListArr.indexOf(cls)];
                    classList = classListArr.join(" ").trim();
                    $(this).attr('class', classList);
                }
            }

        });
    },
    hasSVGClass: function (cls) {
        var el = this[0];
        var classList = $(el).attr('class');
        if (classList) {
            var classListArr = classList.split(" ");
            if (classListArr.indexOf(cls) !== -1) {
                return true;

            } else {
                return false;
            }
        }
        return false;
    }
});

usage :

$('.svg-element').addSVGClass('selected');
Gihan Dilusha
  • 891
  • 4
  • 14
  • 22
-4

Works. But be sure to close the function

$(".node").hover(function(evt){
    console.log($(this).attr("class")); //returns "node"
    console.log($(this).hasClass('node')); //returns false
}, function(){console.log("Done");});

http://jsfiddle.net/X6BPX/

  • Thanks Rick, but I did clarify in the comments that the missing parenthesis was a typo. The function I have on my computer is closed. – Shrey Gupta Jul 24 '13 at 17:48
  • @Rick: how is this "works", when `hasClass('node')` returns false?! – Tom Jul 29 '14 at 14:33
  • completely useless fiddle - it demonstrates that is works for HTML elements. Nobody was questioning that. The issue at hand is that it does not work with SVG elements. (that is, jQuery .hasClass() doesn't.) – mathheadinclouds Apr 09 '15 at 10:37
  • Here is the corrected Fiddle which shows that .hasClass() doesn't work on inline SVG elements. http://jsfiddle.net/X6BPX/8/ – T Nguyen Jun 10 '15 at 01:02