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
This commit is contained in:
Timmy Willison 2015-05-06 13:30:16 -07:00
parent 56bb677725
commit 20aaed367f
2 changed files with 73 additions and 31 deletions

View File

@ -7,16 +7,20 @@ define([
var rclass = /[\t\r\n\f]/g; var rclass = /[\t\r\n\f]/g;
function getClass( elem ) {
return elem.getAttribute && elem.getAttribute( "class" ) || "";
}
jQuery.fn.extend({ jQuery.fn.extend({
addClass: function( value ) { addClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue, var classes, elem, cur, curValue, clazz, j, finalValue,
proceed = typeof value === "string" && value, proceed = typeof value === "string" && value,
i = 0, i = 0,
len = this.length; len = this.length;
if ( jQuery.isFunction( value ) ) { if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) { return this.each(function( j ) {
jQuery( this ).addClass( value.call( this, j, this.className ) ); jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
}); });
} }
@ -26,10 +30,9 @@ jQuery.fn.extend({
for ( ; i < len; i++ ) { for ( ; i < len; i++ ) {
elem = this[ i ]; elem = this[ i ];
cur = elem.nodeType === 1 && ( elem.className ? curValue = getClass( elem );
( " " + elem.className + " " ).replace( rclass, " " ) : cur = elem.nodeType === 1 &&
" " ( " " + curValue + " " ).replace( rclass, " " );
);
if ( cur ) { if ( cur ) {
j = 0; j = 0;
@ -41,8 +44,8 @@ jQuery.fn.extend({
// only assign if different to avoid unneeded rendering. // only assign if different to avoid unneeded rendering.
finalValue = jQuery.trim( cur ); finalValue = jQuery.trim( cur );
if ( elem.className !== finalValue ) { if ( curValue !== finalValue ) {
elem.className = finalValue; elem.setAttribute( "class", finalValue );
} }
} }
} }
@ -52,14 +55,14 @@ jQuery.fn.extend({
}, },
removeClass: function( value ) { removeClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue, var classes, elem, cur, curValue, clazz, j, finalValue,
proceed = arguments.length === 0 || typeof value === "string" && value, proceed = arguments.length === 0 || typeof value === "string" && value,
i = 0, i = 0,
len = this.length; len = this.length;
if ( jQuery.isFunction( value ) ) { if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) { return this.each(function( j ) {
jQuery( this ).removeClass( value.call( this, j, this.className ) ); jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
}); });
} }
if ( proceed ) { if ( proceed ) {
@ -67,11 +70,11 @@ jQuery.fn.extend({
for ( ; i < len; i++ ) { for ( ; i < len; i++ ) {
elem = this[ i ]; elem = this[ i ];
curValue = getClass( elem );
// This expression is here for better compressibility (see addClass) // This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( elem.className ? cur = elem.nodeType === 1 &&
( " " + elem.className + " " ).replace( rclass, " " ) : ( " " + curValue + " " ).replace( rclass, " " );
""
);
if ( cur ) { if ( cur ) {
j = 0; j = 0;
@ -84,8 +87,8 @@ jQuery.fn.extend({
// Only assign if different to avoid unneeded rendering. // Only assign if different to avoid unneeded rendering.
finalValue = value ? jQuery.trim( cur ) : ""; finalValue = value ? jQuery.trim( cur ) : "";
if ( elem.className !== finalValue ) { if ( curValue !== finalValue ) {
elem.className = finalValue; elem.setAttribute( "class", finalValue );
} }
} }
} }
@ -104,20 +107,24 @@ jQuery.fn.extend({
if ( jQuery.isFunction( value ) ) { if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) { return this.each(function( i ) {
jQuery( this ).toggleClass( jQuery( this ).toggleClass(
value.call(this, i, this.className, stateVal), stateVal value.call( this, i, getClass( this ), stateVal ),
stateVal
); );
}); });
} }
return this.each(function() { return this.each(function() {
var className, i, self, classNames;
if ( type === "string" ) { if ( type === "string" ) {
// Toggle individual class names // Toggle individual class names
var className, i = 0;
i = 0, self = jQuery( this );
self = jQuery( this ),
classNames = value.match( rnotwhite ) || []; classNames = value.match( rnotwhite ) || [];
while ( ( className = classNames[ i++ ] ) ) { while ( ( className = classNames[ i++ ] ) ) {
// Check each className given, space separated list // Check each className given, space separated list
if ( self.hasClass( className ) ) { if ( self.hasClass( className ) ) {
self.removeClass( className ); self.removeClass( className );
@ -128,18 +135,24 @@ jQuery.fn.extend({
// Toggle whole class name // Toggle whole class name
} else if ( value === undefined || type === "boolean" ) { } else if ( value === undefined || type === "boolean" ) {
if ( this.className ) { className = getClass( this );
if ( className ) {
// store className if set // store className if set
dataPriv.set( this, "__className__", this.className ); dataPriv.set( this, "__className__", className );
} }
// If the element has a class name or if we're passed `false`, // If the element has a class name or if we're passed `false`,
// then remove the whole classname (if there was one, the above saved it). // then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything), // Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored. // falling back to the empty string if nothing was stored.
this.className = this.className || value === false ? if ( this.setAttribute ) {
this.setAttribute( "class",
className || value === false ?
"" : "" :
dataPriv.get( this, "__className__" ) || ""; dataPriv.get( this, "__className__" ) || ""
);
}
} }
}); });
}, },
@ -150,8 +163,9 @@ jQuery.fn.extend({
l = this.length; l = this.length;
for ( ; i < l; i++ ) { for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && if ( this[i].nodeType === 1 &&
(" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { ( " " + getClass( this[i] ) + " " ).replace( rclass, " " )
.indexOf( className ) > -1
) {
return true; return true;
} }
} }

View File

@ -1478,3 +1478,31 @@ test( "Insignificant white space returned for $(option).val() (#14858)", functio
val = jQuery( "<option> test </option>" ).val(); val = jQuery( "<option> test </option>" ).val();
equal( val.length, 4, "insignificant white-space returned for value" ); equal( val.length, 4, "insignificant white-space returned for value" );
}); });
test( "SVG class manipulation (gh-2199)", function() {
expect( 12 );
function createSVGElement( nodeName ) {
return document.createElementNS( "http://www.w3.org/2000/svg", nodeName );
}
jQuery.each([
"svg",
"rect",
"g"
], function() {
var elem = jQuery( createSVGElement( this ) );
elem.addClass( "awesome" );
ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") has added class" );
elem.removeClass( "awesome" );
ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") removes the class" );
elem.toggleClass( "awesome" );
ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class on" );
elem.toggleClass( "awesome" );
ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class off" );
});
});