]> source.dussan.org Git - jquery.git/commitdiff
CSS: Skip falsy values in `addClass( array )`, compress code
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Mon, 24 Jan 2022 17:56:49 +0000 (18:56 +0100)
committerGitHub <noreply@github.com>
Mon, 24 Jan 2022 17:56:49 +0000 (18:56 +0100)
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

src/attributes/classes.js
test/unit/attributes.js

index 796fbcc808ca15bbe771f8c9c1a7bab3388f6128..5e64ea2ae42525e9fd7bd3ddac5a1c0c70aa507a 100644 (file)
@@ -20,8 +20,7 @@ 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 ) {
@@ -29,36 +28,35 @@ jQuery.fn.extend( {
                        } );
                }
 
-               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 ) {
@@ -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(
@@ -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 ) {
index 98fae06c7efb8a056becb75e3d47033462996a2e..5f2418c7e34e1034e178bed31a79ca9496e06618 100644 (file)
@@ -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 );