Skip to content

Commit a338b40

Browse files
authored
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
1 parent 9c6f64c commit a338b40

File tree

2 files changed

+82
-40
lines changed

2 files changed

+82
-40
lines changed

src/attributes/classes.js

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,43 @@ function classesToArray( value ) {
2020

2121
jQuery.fn.extend( {
2222
addClass: function( value ) {
23-
var classes, elem, cur, curValue, clazz, j, finalValue,
24-
i = 0;
23+
var classNames, cur, curValue, className, i, finalValue;
2524

2625
if ( typeof value === "function" ) {
2726
return this.each( function( j ) {
2827
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
2928
} );
3029
}
3130

32-
classes = classesToArray( value );
31+
classNames = classesToArray( value );
3332

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

3938
if ( cur ) {
40-
j = 0;
41-
while ( ( clazz = classes[ j++ ] ) ) {
42-
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
43-
cur += clazz + " ";
39+
for ( i = 0; i < classNames.length; i++ ) {
40+
className = classNames[ i ];
41+
if ( cur.indexOf( " " + className + " " ) < 0 ) {
42+
cur += className + " ";
4443
}
4544
}
4645

4746
// Only assign if different to avoid unneeded rendering.
4847
finalValue = stripAndCollapse( cur );
4948
if ( curValue !== finalValue ) {
50-
elem.setAttribute( "class", finalValue );
49+
this.setAttribute( "class", finalValue );
5150
}
5251
}
53-
}
52+
} );
5453
}
5554

5655
return this;
5756
},
5857

5958
removeClass: function( value ) {
60-
var classes, elem, cur, curValue, clazz, j, finalValue,
61-
i = 0;
59+
var classNames, cur, curValue, className, i, finalValue;
6260

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

73-
classes = classesToArray( value );
71+
classNames = classesToArray( value );
7472

75-
if ( classes.length ) {
76-
while ( ( elem = this[ i++ ] ) ) {
77-
curValue = getClass( elem );
73+
if ( classNames.length ) {
74+
return this.each( function() {
75+
curValue = getClass( this );
7876

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

8280
if ( cur ) {
83-
j = 0;
84-
while ( ( clazz = classes[ j++ ] ) ) {
81+
for ( i = 0; i < classNames.length; i++ ) {
82+
className = classNames[ i ];
8583

8684
// Remove *all* instances
87-
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
88-
cur = cur.replace( " " + clazz + " ", " " );
85+
while ( cur.indexOf( " " + className + " " ) > -1 ) {
86+
cur = cur.replace( " " + className + " ", " " );
8987
}
9088
}
9189

9290
// Only assign if different to avoid unneeded rendering.
9391
finalValue = stripAndCollapse( cur );
9492
if ( curValue !== finalValue ) {
95-
elem.setAttribute( "class", finalValue );
93+
this.setAttribute( "class", finalValue );
9694
}
9795
}
98-
}
96+
} );
9997
}
10098

10199
return this;
102100
},
103101

104102
toggleClass: function( value, stateVal ) {
103+
var classNames, className, i, self;
104+
105105
if ( typeof value === "function" ) {
106106
return this.each( function( i ) {
107107
jQuery( this ).toggleClass(
@@ -115,24 +115,28 @@ jQuery.fn.extend( {
115115
return stateVal ? this.addClass( value ) : this.removeClass( value );
116116
}
117117

118-
return this.each( function() {
119-
var className, i, self, classNames;
118+
classNames = classesToArray( value );
120119

121-
// Toggle individual class names
122-
i = 0;
123-
self = jQuery( this );
124-
classNames = classesToArray( value );
120+
if ( classNames.length ) {
121+
return this.each( function() {
125122

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

128-
// Check each className given, space separated list
129-
if ( self.hasClass( className ) ) {
130-
self.removeClass( className );
131-
} else {
132-
self.addClass( className );
126+
for ( i = 0; i < classNames.length; i++ ) {
127+
className = classNames[ i ];
128+
129+
// Check each className given, space separated list
130+
if ( self.hasClass( className ) ) {
131+
self.removeClass( className );
132+
} else {
133+
self.addClass( className );
134+
}
133135
}
134-
}
135-
} );
136+
} );
137+
}
138+
139+
return this;
136140
},
137141

138142
hasClass: function( selector ) {

test/unit/attributes.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,44 @@ QUnit.test( "addClass, removeClass, hasClass on elements with classes with non-H
16441644
testMatches();
16451645
} );
16461646

1647+
( function() {
1648+
var rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
1649+
1650+
function expectClasses( assert, elem, classes ) {
1651+
var actualClassesSorted = ( elem.attr( "class" ).match( rnothtmlwhite ) || [] )
1652+
.sort().join( " " );
1653+
var classesSorted = classes.slice()
1654+
.sort().join( " " );
1655+
assert.equal( actualClassesSorted, classesSorted, "Expected classes present" );
1656+
}
1657+
1658+
QUnit.test( "addClass on arrays with falsy elements (gh-4998)", function( assert ) {
1659+
assert.expect( 3 );
1660+
1661+
var elem = jQuery( "<div class='a'></div>" );
1662+
1663+
elem.addClass( [ "b", "", "c" ] );
1664+
expectClasses( assert, elem, [ "a", "b", "c" ] );
1665+
elem.addClass( [ "", "d" ] );
1666+
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
1667+
elem.addClass( [ "e", "" ] );
1668+
expectClasses( assert, elem, [ "a", "b", "c", "d", "e" ] );
1669+
} );
1670+
1671+
QUnit.test( "removeClass on arrays with falsy elements (gh-4998)", function( assert ) {
1672+
assert.expect( 3 );
1673+
1674+
var elem = jQuery( "<div class='a b c d e'></div>" );
1675+
1676+
elem.removeClass( [ "e", "" ] );
1677+
expectClasses( assert, elem, [ "a", "b", "c", "d" ] );
1678+
elem.removeClass( [ "", "d" ] );
1679+
expectClasses( assert, elem, [ "a", "b", "c" ] );
1680+
elem.removeClass( [ "b", "", "c" ] );
1681+
expectClasses( assert, elem, [ "a" ] );
1682+
} );
1683+
} )();
1684+
16471685
QUnit.test( "contents().hasClass() returns correct values", function( assert ) {
16481686
assert.expect( 2 );
16491687

0 commit comments

Comments
 (0)