Skip to content

Commit b5b0d72

Browse files
committed
Attributes: add SVG class manipulation
- Note: support for SVG is limited in jQuery, but this is one area where the cost vs benefit ratio was acceptable. Fixes gh-2199 Close gh-2268
1 parent cbd51c5 commit b5b0d72

File tree

2 files changed

+75
-33
lines changed

2 files changed

+75
-33
lines changed

src/attributes/classes.js

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ define([
66

77
var rclass = /[\t\r\n\f]/g;
88

9+
function getClass( elem ) {
10+
return elem.getAttribute && elem.getAttribute( "class" ) || "";
11+
}
12+
913
jQuery.fn.extend({
1014
addClass: function( value ) {
11-
var classes, elem, cur, clazz, j, finalValue,
15+
var classes, elem, cur, curValue, clazz, j, finalValue,
1216
i = 0,
1317
len = this.length,
1418
proceed = typeof value === "string" && value;
1519

1620
if ( jQuery.isFunction( value ) ) {
1721
return this.each(function( j ) {
18-
jQuery( this ).addClass( value.call( this, j, this.className ) );
22+
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
1923
});
2024
}
2125

@@ -25,10 +29,9 @@ jQuery.fn.extend({
2529

2630
for ( ; i < len; i++ ) {
2731
elem = this[ i ];
28-
cur = elem.nodeType === 1 && ( elem.className ?
29-
( " " + elem.className + " " ).replace( rclass, " " ) :
30-
" "
31-
);
32+
curValue = getClass( elem );
33+
cur = elem.nodeType === 1 &&
34+
( " " + curValue + " " ).replace( rclass, " " );
3235

3336
if ( cur ) {
3437
j = 0;
@@ -40,8 +43,8 @@ jQuery.fn.extend({
4043

4144
// only assign if different to avoid unneeded rendering.
4245
finalValue = jQuery.trim( cur );
43-
if ( elem.className !== finalValue ) {
44-
elem.className = finalValue;
46+
if ( curValue !== finalValue ) {
47+
elem.setAttribute( "class", finalValue );
4548
}
4649
}
4750
}
@@ -51,26 +54,26 @@ jQuery.fn.extend({
5154
},
5255

5356
removeClass: function( value ) {
54-
var classes, elem, cur, clazz, j, finalValue,
57+
var classes, elem, cur, curValue, clazz, j, finalValue,
5558
i = 0,
5659
len = this.length,
5760
proceed = arguments.length === 0 || typeof value === "string" && value;
5861

5962
if ( jQuery.isFunction( value ) ) {
6063
return this.each(function( j ) {
61-
jQuery( this ).removeClass( value.call( this, j, this.className ) );
64+
jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
6265
});
6366
}
6467
if ( proceed ) {
6568
classes = ( value || "" ).match( rnotwhite ) || [];
6669

6770
for ( ; i < len; i++ ) {
6871
elem = this[ i ];
72+
curValue = getClass( elem );
73+
6974
// This expression is here for better compressibility (see addClass)
70-
cur = elem.nodeType === 1 && ( elem.className ?
71-
( " " + elem.className + " " ).replace( rclass, " " ) :
72-
""
73-
);
75+
cur = elem.nodeType === 1 &&
76+
( " " + curValue + " " ).replace( rclass, " " );
7477

7578
if ( cur ) {
7679
j = 0;
@@ -83,8 +86,8 @@ jQuery.fn.extend({
8386

8487
// only assign if different to avoid unneeded rendering.
8588
finalValue = value ? jQuery.trim( cur ) : "";
86-
if ( elem.className !== finalValue ) {
87-
elem.className = finalValue;
89+
if ( curValue !== finalValue ) {
90+
elem.setAttribute( "class", finalValue );
8891
}
8992
}
9093
}
@@ -103,21 +106,25 @@ jQuery.fn.extend({
103106
if ( jQuery.isFunction( value ) ) {
104107
return this.each(function( i ) {
105108
jQuery( this ).toggleClass(
106-
value.call(this, i, this.className, stateVal), stateVal
109+
value.call( this, i, getClass( this ), stateVal ),
110+
stateVal
107111
);
108112
});
109113
}
110114

111115
return this.each(function() {
116+
var className, i, self, classNames;
117+
112118
if ( type === "string" ) {
113-
// toggle individual class names
114-
var className,
115-
i = 0,
116-
self = jQuery( this ),
117-
classNames = value.match( rnotwhite ) || [];
118-
119-
while ( (className = classNames[ i++ ]) ) {
120-
// check each className given, space separated list
119+
120+
// Toggle individual class names
121+
i = 0;
122+
self = jQuery( this );
123+
classNames = value.match( rnotwhite ) || [];
124+
125+
while ( ( className = classNames[ i++ ] ) ) {
126+
127+
// Check each className given, space separated list
121128
if ( self.hasClass( className ) ) {
122129
self.removeClass( className );
123130
} else {
@@ -126,19 +133,25 @@ jQuery.fn.extend({
126133
}
127134

128135
// Toggle whole class name
129-
} else if ( type === "undefined" || type === "boolean" ) {
130-
if ( this.className ) {
136+
} else if ( value === undefined || type === "boolean" ) {
137+
className = getClass( this );
138+
if ( className ) {
139+
131140
// store className if set
132-
jQuery._data( this, "__className__", this.className );
141+
jQuery._data( this, "__className__", className );
133142
}
134143

135144
// If the element has a class name or if we're passed "false",
136145
// then remove the whole classname (if there was one, the above saved it).
137146
// Otherwise bring back whatever was previously saved (if anything),
138147
// falling back to the empty string if nothing was stored.
139-
this.className = this.className || value === false ?
140-
"" :
141-
jQuery._data( this, "__className__" ) || "";
148+
if ( this.setAttribute ) {
149+
this.setAttribute( "class",
150+
className || value === false ?
151+
"" :
152+
jQuery._data( this, "__className__" ) || ""
153+
);
154+
}
142155
}
143156
});
144157
},
@@ -149,8 +162,9 @@ jQuery.fn.extend({
149162
l = this.length;
150163
for ( ; i < l; i++ ) {
151164
if ( this[i].nodeType === 1 &&
152-
(" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
153-
165+
( " " + getClass( this[i] ) + " " ).replace( rclass, " " )
166+
.indexOf( className ) > -1
167+
) {
154168
return true;
155169
}
156170
}

test/unit/attributes.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,3 +1476,31 @@ test( "Insignificant white space returned for $(option).val() (#14858)", functio
14761476
val = jQuery( "<option> test </option>" ).val();
14771477
equal( val.length, 4, "insignificant white-space returned for value" );
14781478
});
1479+
1480+
test( "SVG class manipulation (gh-2199)", function() {
1481+
expect( 12 );
1482+
1483+
function createSVGElement( nodeName ) {
1484+
return document.createElementNS( "http://www.w3.org/2000/svg", nodeName );
1485+
}
1486+
1487+
jQuery.each([
1488+
"svg",
1489+
"rect",
1490+
"g"
1491+
], function() {
1492+
var elem = jQuery( createSVGElement( this ) );
1493+
1494+
elem.addClass( "awesome" );
1495+
ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") has added class" );
1496+
1497+
elem.removeClass( "awesome" );
1498+
ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") removes the class" );
1499+
1500+
elem.toggleClass( "awesome" );
1501+
ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class on" );
1502+
1503+
elem.toggleClass( "awesome" );
1504+
ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class off" );
1505+
});
1506+
});

0 commit comments

Comments
 (0)