Skip to content
Permalink
Browse files

Attributes: allow array param in add/remove/toggleClass

+30 bytes instead of +182

Thanks to @faisaliyk for the first pass on this feature.

Fixes gh-3532
Close gh-3917
  • Loading branch information
timmywil committed Jan 8, 2018
1 parent a88b48e commit 80f57f8a13debaab87b99f73631669699da3e1a5
Showing with 69 additions and 8 deletions.
  1. +19 −8 src/attributes/classes.js
  2. +50 −0 test/unit/attributes.js
@@ -12,6 +12,16 @@ function getClass( elem ) {
return elem.getAttribute && elem.getAttribute( "class" ) || "";
}

function classesToArray( value ) {
if ( Array.isArray( value ) ) {
return value;
}
if ( typeof value === "string" ) {
return value.match( rnothtmlwhite ) || [];
}
return [];
}

jQuery.fn.extend( {
addClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
@@ -23,9 +33,9 @@ jQuery.fn.extend( {
} );
}

if ( typeof value === "string" && value ) {
classes = value.match( rnothtmlwhite ) || [];
classes = classesToArray( value );

if ( classes.length ) {
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
@@ -64,9 +74,9 @@ jQuery.fn.extend( {
return this.attr( "class", "" );
}

if ( typeof value === "string" && value ) {
classes = value.match( rnothtmlwhite ) || [];
classes = classesToArray( value );

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

@@ -96,9 +106,10 @@ jQuery.fn.extend( {
},

toggleClass: function( value, stateVal ) {
var type = typeof value;
var type = typeof value,
isValidValue = type === "string" || Array.isArray( value );

if ( typeof stateVal === "boolean" && type === "string" ) {
if ( typeof stateVal === "boolean" && isValidValue ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}

@@ -114,12 +125,12 @@ jQuery.fn.extend( {
return this.each( function() {
var className, i, self, classNames;

if ( type === "string" ) {
if ( isValidValue ) {

// Toggle individual class names
i = 0;
self = jQuery( this );
classNames = value.match( rnothtmlwhite ) || [];
classNames = classesToArray( value );

while ( ( className = classNames[ i++ ] ) ) {

@@ -12,6 +12,10 @@ function functionReturningObj( value ) {
};
}

function arrayFromString( value ) {
return value ? value.split( " " ) : [];
}

/*
======== local reference =======
bareObj and functionReturningObj can be used to test passing functions to setters
@@ -1261,6 +1265,10 @@ QUnit.test( "addClass(Function)", function( assert ) {
testAddClass( functionReturningObj, assert );
} );

QUnit.test( "addClass(Array)", function( assert ) {
testAddClass( arrayFromString, assert );
} );

QUnit.test( "addClass(Function) with incoming value", function( assert ) {
assert.expect( 52 );
var pass, i,
@@ -1334,6 +1342,10 @@ QUnit.test( "removeClass(Function) - simple", function( assert ) {
testRemoveClass( functionReturningObj, assert );
} );

QUnit.test( "removeClass(Array) - simple", function( assert ) {
testRemoveClass( arrayFromString, assert );
} );

QUnit.test( "removeClass(Function) with incoming value", function( assert ) {
assert.expect( 52 );

@@ -1432,6 +1444,10 @@ QUnit.test( "toggleClass(Function[, boolean])", function( assert ) {
testToggleClass( functionReturningObj, assert );
} );

QUnit.test( "toggleClass(Array[, boolean])", function( assert ) {
testToggleClass( arrayFromString, assert );
} );

QUnit.test( "toggleClass(Function[, boolean]) with incoming value", function( assert ) {
assert.expect( 14 );

@@ -1567,6 +1583,40 @@ QUnit.test( "addClass, removeClass, hasClass on many elements", function( assert
"Did not find a class when not present" );
} );

QUnit.test( "addClass, removeClass, hasClass on many elements - Array", function( assert ) {
assert.expect( 16 );

var elem = jQuery( "<p>p0</p><p>p1</p><p>p2</p>" );

elem.addClass( [ "hi" ] );
assert.equal( elem[ 0 ].className, "hi", "Check single added class" );
assert.equal( elem[ 1 ].className, "hi", "Check single added class" );
assert.equal( elem[ 2 ].className, "hi", "Check single added class" );

elem.addClass( [ "foo", "bar" ] );
assert.equal( elem[ 0 ].className, "hi foo bar", "Check more added classes" );
assert.equal( elem[ 1 ].className, "hi foo bar", "Check more added classes" );
assert.equal( elem[ 2 ].className, "hi foo bar", "Check more added classes" );

elem.removeClass();
assert.equal( elem[ 0 ].className, "", "Remove all classes" );
assert.equal( elem[ 1 ].className, "", "Remove all classes" );
assert.equal( elem[ 2 ].className, "", "Remove all classes" );

elem.addClass( [ "hi", "foo", "bar", "baz" ] );
elem.removeClass( [ "foo" ] );
assert.equal( elem[ 0 ].className, "hi bar baz", "Check removal of one class" );
assert.equal( elem[ 1 ].className, "hi bar baz", "Check removal of one class" );
assert.equal( elem[ 2 ].className, "hi bar baz", "Check removal of one class" );

elem.removeClass( [ "bar baz" ] );
assert.equal( elem[ 0 ].className, "hi", "Check removal of two classes" );
assert.equal( elem[ 1 ].className, "hi", "Check removal of two classes" );
assert.equal( elem[ 2 ].className, "hi", "Check removal of two classes" );

assert.ok( elem.hasClass( "hi" ), "Check has1" );
} );

QUnit.test( "addClass, removeClass, hasClass on elements with classes with non-HTML whitespace (gh-3072, gh-3003)", function( assert ) {
assert.expect( 9 );

2 comments on commit 80f57f8

@christopheroussy

This comment has been minimized.

Copy link

@christopheroussy christopheroussy replied Jul 6, 2020

Nice addition, but this should also work with the element creation shortform, not just with .addClass:
$("<span/>", {"class" : ["a", "b", "c"]}));
Results in:
<span class="a,b,c"></span> instead of <span class="a b c"/> which is not intuitive and will fail silently.

@dmethvin

This comment has been minimized.

Copy link
Member

@dmethvin dmethvin replied Jul 6, 2020

The short form doesn't work that way. There is no special case code for it, other than calling .attr() if there's no jQuery method. I agree it's not intuitive and for that reason it's better to not use the "short form" at all, especially since it's not even that much shorter. Perhaps we should mark it as deprecated to emphasize that.

Please sign in to comment.