Skip to content

Commit 9b34bdb

Browse files
committed
CSS: Skip falsy values in addClass( array ), compress code
This change makes jQuery skip falsy values in `addClass( array )` & `removeClass( array )` instead of stopping iteration when the first falsy value is detected. This makes code like: ```js elem.addClass( [ "a", "", "b" ] ); ``` add both the `a` & `b` classes. The code was also optimized for size a bit so it doesn't increase the minified gzipped size. Fixes gh-4998 Closes gh-5003 (partially cherry picked from commit a338b40)
1 parent 95e34b6 commit 9b34bdb

File tree

2 files changed

+72
-36
lines changed

2 files changed

+72
-36
lines changed

src/attributes/classes.js

+34-36
Original file line numberDiff line numberDiff line change
@@ -25,45 +25,43 @@ function classesToArray( value ) {
2525

2626
jQuery.fn.extend( {
2727
addClass: function( value ) {
28-
var classes, elem, cur, curValue, clazz, j, finalValue,
29-
i = 0;
28+
var classNames, cur, curValue, className, i, finalValue;
3029

3130
if ( isFunction( value ) ) {
3231
return this.each( function( j ) {
3332
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
3433
} );
3534
}
3635

37-
classes = classesToArray( value );
36+
classNames = classesToArray( value );
3837

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

4443
if ( cur ) {
45-
j = 0;
46-
while ( ( clazz = classes[ j++ ] ) ) {
47-
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
48-
cur += clazz + " ";
44+
for ( i = 0; i < classNames.length; i++ ) {
45+
className = classNames[ i ];
46+
if ( cur.indexOf( " " + className + " " ) < 0 ) {
47+
cur += className + " ";
4948
}
5049
}
5150

5251
// Only assign if different to avoid unneeded rendering.
5352
finalValue = stripAndCollapse( cur );
5453
if ( curValue !== finalValue ) {
55-
elem.setAttribute( "class", finalValue );
54+
this.setAttribute( "class", finalValue );
5655
}
5756
}
58-
}
57+
} );
5958
}
6059

6160
return this;
6261
},
6362

6463
removeClass: function( value ) {
65-
var classes, elem, cur, curValue, clazz, j, finalValue,
66-
i = 0;
64+
var classNames, cur, curValue, className, i, finalValue;
6765

6866
if ( isFunction( value ) ) {
6967
return this.each( function( j ) {
@@ -75,45 +73,42 @@ jQuery.fn.extend( {
7573
return this.attr( "class", "" );
7674
}
7775

78-
classes = classesToArray( value );
76+
classNames = classesToArray( value );
7977

80-
if ( classes.length ) {
81-
while ( ( elem = this[ i++ ] ) ) {
82-
curValue = getClass( elem );
78+
if ( classNames.length ) {
79+
return this.each( function() {
80+
curValue = getClass( this );
8381

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

8785
if ( cur ) {
88-
j = 0;
89-
while ( ( clazz = classes[ j++ ] ) ) {
86+
for ( i = 0; i < classNames.length; i++ ) {
87+
className = classNames[ i ];
9088

9189
// Remove *all* instances
92-
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
93-
cur = cur.replace( " " + clazz + " ", " " );
90+
while ( cur.indexOf( " " + className + " " ) > -1 ) {
91+
cur = cur.replace( " " + className + " ", " " );
9492
}
9593
}
9694

9795
// Only assign if different to avoid unneeded rendering.
9896
finalValue = stripAndCollapse( cur );
9997
if ( curValue !== finalValue ) {
100-
elem.setAttribute( "class", finalValue );
98+
this.setAttribute( "class", finalValue );
10199
}
102100
}
103-
}
101+
} );
104102
}
105103

106104
return this;
107105
},
108106

109107
toggleClass: function( value, stateVal ) {
110-
var type = typeof value,
108+
var classNames, className, i, self,
109+
type = typeof value,
111110
isValidValue = type === "string" || Array.isArray( value );
112111

113-
if ( typeof stateVal === "boolean" && isValidValue ) {
114-
return stateVal ? this.addClass( value ) : this.removeClass( value );
115-
}
116-
117112
if ( isFunction( value ) ) {
118113
return this.each( function( i ) {
119114
jQuery( this ).toggleClass(
@@ -123,17 +118,20 @@ jQuery.fn.extend( {
123118
} );
124119
}
125120

126-
return this.each( function() {
127-
var className, i, self, classNames;
121+
if ( typeof stateVal === "boolean" && isValidValue ) {
122+
return stateVal ? this.addClass( value ) : this.removeClass( value );
123+
}
128124

125+
classNames = classesToArray( value );
126+
127+
return this.each( function() {
129128
if ( isValidValue ) {
130129

131130
// Toggle individual class names
132-
i = 0;
133131
self = jQuery( this );
134-
classNames = classesToArray( value );
135132

136-
while ( ( className = classNames[ i++ ] ) ) {
133+
for ( i = 0; i < classNames.length; i++ ) {
134+
className = classNames[ i ];
137135

138136
// Check each className given, space separated list
139137
if ( self.hasClass( className ) ) {

test/unit/attributes.js

+38
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,44 @@ QUnit.test( "addClass, removeClass, hasClass on elements with classes with non-H
16311631
testMatches();
16321632
} );
16331633

1634+
( function() {
1635+
var rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
1636+
1637+
function expectClasses( assert, elem, classes ) {
1638+
var actualClassesSorted = ( elem.attr( "class" ).match( rnothtmlwhite ) || [] )
1639+
.sort().join( " " );
1640+
var classesSorted = classes.slice()
1641+
.sort().join( " " );
1642+
assert.equal( actualClassesSorted, classesSorted, "Expected classes present" );
1643+
}
1644+
1645+
QUnit.test( "addClass on arrays with falsy elements (gh-4998)", function( assert ) {
1646+
assert.expect( 3 );
1647+
1648+
var elem = jQuery( "<div class='a'></div>" );
1649+
1650+
elem.addClass( [ "b", "", "c" ] );
1651+
expectClasses( assert, elem, [ "a", "b", "c" ] );
1652+
elem.addClass( [ "", "d" ] );
1653+
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
1654+
elem.addClass( [ "e", "" ] );
1655+
expectClasses( assert, elem, [ "a", "b", "c", "d", "e" ] );
1656+
} );
1657+
1658+
QUnit.test( "removeClass on arrays with falsy elements (gh-4998)", function( assert ) {
1659+
assert.expect( 3 );
1660+
1661+
var elem = jQuery( "<div class='a b c d e'></div>" );
1662+
1663+
elem.removeClass( [ "e", "" ] );
1664+
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
1665+
elem.removeClass( [ "", "d" ] );
1666+
expectClasses( assert, elem, [ "a", "b", "c" ] );
1667+
elem.removeClass( [ "b", "", "c" ] );
1668+
expectClasses( assert, elem, [ "a" ] );
1669+
} );
1670+
} )();
1671+
16341672
QUnit.test( "contents().hasClass() returns correct values", function( assert ) {
16351673
assert.expect( 2 );
16361674

0 commit comments

Comments
 (0)