Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 44 additions & 40 deletions src/attributes/classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,43 @@ function classesToArray( value ) {

jQuery.fn.extend( {
addClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
i = 0;
var classNames, cur, curValue, className, i, finalValue;

if ( typeof value === "function" ) {
return this.each( function( j ) {
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
} );
}

classes = classesToArray( value );
classNames = classesToArray( value );

if ( classes.length ) {
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( classNames.length ) {
return this.each( function() {
curValue = getClass( this );
cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

if ( cur ) {
j = 0;
while ( ( clazz = classes[ j++ ] ) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
for ( i = 0; i < classNames.length; i++ ) {
className = classNames[ i ];
if ( cur.indexOf( " " + className + " " ) < 0 ) {
cur += className + " ";
}
}

// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue );
this.setAttribute( "class", finalValue );
}
}
}
} );
}

return this;
},

removeClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
i = 0;
var classNames, cur, curValue, className, i, finalValue;

if ( typeof value === "function" ) {
return this.each( function( j ) {
Expand All @@ -70,38 +68,40 @@ jQuery.fn.extend( {
return this.attr( "class", "" );
}

classes = classesToArray( value );
classNames = classesToArray( value );

if ( classes.length ) {
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
if ( classNames.length ) {
return this.each( function() {
curValue = getClass( this );

// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

if ( cur ) {
j = 0;
while ( ( clazz = classes[ j++ ] ) ) {
for ( i = 0; i < classNames.length; i++ ) {
className = classNames[ i ];

// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
cur = cur.replace( " " + clazz + " ", " " );
while ( cur.indexOf( " " + className + " " ) > -1 ) {
cur = cur.replace( " " + className + " ", " " );
}
}

// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue );
this.setAttribute( "class", finalValue );
}
}
}
} );
}

return this;
},

toggleClass: function( value, stateVal ) {
var classNames, className, i, self;

if ( typeof value === "function" ) {
return this.each( function( i ) {
jQuery( this ).toggleClass(
Expand All @@ -115,24 +115,28 @@ jQuery.fn.extend( {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}

return this.each( function() {
var className, i, self, classNames;
classNames = classesToArray( value );

// Toggle individual class names
i = 0;
self = jQuery( this );
classNames = classesToArray( value );
if ( classNames.length ) {
return this.each( function() {

while ( ( className = classNames[ i++ ] ) ) {
// Toggle individual class names
self = jQuery( this );

// Check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
for ( i = 0; i < classNames.length; i++ ) {
className = classNames[ i ];

// Check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
}
}
}
} );
} );
}

return this;
},

hasClass: function( selector ) {
Expand Down
38 changes: 38 additions & 0 deletions test/unit/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,44 @@ QUnit.test( "addClass, removeClass, hasClass on elements with classes with non-H
testMatches();
} );

( function() {
var rnothtmlwhite = /[^\x20\t\r\n\f]+/g;

function expectClasses( assert, elem, classes ) {
var actualClassesSorted = ( elem.attr( "class" ).match( rnothtmlwhite ) || [] )
.sort().join( " " );
var classesSorted = classes.slice()
.sort().join( " " );
assert.equal( actualClassesSorted, classesSorted, "Expected classes present" );
}

QUnit.test( "addClass on arrays with falsy elements (gh-4998)", function( assert ) {
assert.expect( 3 );

var elem = jQuery( "<div class='a'></div>" );

elem.addClass( [ "b", "", "c" ] );
expectClasses( assert, elem, [ "a", "b", "c" ] );
elem.addClass( [ "", "d" ] );
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
elem.addClass( [ "e", "" ] );
expectClasses( assert, elem, [ "a", "b", "c", "d", "e" ] );
} );

QUnit.test( "removeClass on arrays with falsy elements (gh-4998)", function( assert ) {
assert.expect( 3 );

var elem = jQuery( "<div class='a b c d e'></div>" );

elem.removeClass( [ "e", "" ] );
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
elem.removeClass( [ "", "d" ] );
expectClasses( assert, elem, [ "a", "b", "c" ] );
elem.removeClass( [ "b", "", "c" ] );
expectClasses( assert, elem, [ "a" ] );
} );
} )();

QUnit.test( "contents().hasClass() returns correct values", function( assert ) {
assert.expect( 2 );

Expand Down