classList APIの複数引数について


## 導入

classList APIのメソッドでの複数引数の扱いについて情報が見つからなかったため、自分で調べた結果を書いていきます。
~~[MDN](https://developer.mozilla.org/ja/docs/DOM/element.classList)にもこれに関する情報は書いていないため、これがW3Cの仕様として正しいものなのかは判断できません。
意見をお願いします。~~

<ins>仕様でこのようになっているようです。
[追記](#%E8%BF%BD%E8%A8%98)</ins>

## 検証環境

<dl>
<dt>OS</dt>
<dd>Windows 7 Home Premium 64bit</dd>
<dt>Web ブラウザ</dt>
<dd>Google Chrome 38.0.2125.111 m</dd>
<dt>環境</dt>
<dd>JavaScript コンソール</dd>
</dl>

## 各メソッドの複数引数の扱い

### classList.add

引数を複数指定することで、複数のクラスを追加できるようです。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を確認する。
 * 未指定なので、空文字となる。
 */
console.log(classList.toString());
// 


/**
 * 通常のクラス指定。
 * 正しくクラスが追加される。
 */
classList.add('ex1-1');
console.log(classList.toString());
// ex1-1


/**
 * 複数引数によるクラス指定。
 * それぞれのクラスが指定される。
 */
classList.add('ex2-1', 'ex2-2');
console.log(classList.toString());
// ex1-1 ex2-1 ex2-2


/**
 * 第三引数への指定も、有効に動作する。
 */
classList.add('ex3-1', 'ex3-2', 'ex3-3');
console.log(classList.toString());
// ex1-1 ex2-1 ex2-2 ex3-1 ex3-2 ex3-3
```

引数にスペースが入ると、エラーになります。
このため、jQueryのようにスペース区切りで指定することは出来ません。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を確認する。
 * 未指定なので、空文字となる。
 */
console.log(classList.toString());
// 


/**
 * スペース区切りのクラス指定を試みる。
 * エラーとなり、クラスの追加は行われない。
 */
try {
    classList.add('ex1-1 ex1-2');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'add' on 'DOMTokenList': The token provided ('ex1-1 ex1-2') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// 


/**
 * 指定値の末端にスペースを追加。
 * これもエラーとなり、クラスの追加は行われない。
 */
try {
    classList.add('ex2-1 ');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'add' on 'DOMTokenList': The token provided ('ex2-1 ') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// 


/**
 * 指定値の先頭にスペースを追加した場合も同様。
 */
try {
    classList.add(' ex3-1');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'add' on 'DOMTokenList': The token provided (' ex3-1') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// 
```

### classList.remove

`classList.add`メソッドと同じく、引数を複数指定することで、複数のクラスを削除できるようです。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1-1', 'ex2-1', 'ex2-2', 'ex3-1', 'ex3-2', 'ex3-3');
console.log(classList.toString());
// ex1-1 ex2-1 ex2-2 ex3-1 ex3-2 ex3-3


/**
 * 通常のクラスの削除。
 * 正しくクラスが削除される。
 */
classList.remove('ex1-1');
console.log(classList.toString());
// ex2-1 ex2-2 ex3-1 ex3-2 ex3-3


/**
 * 複数引数によるクラスの削除。
 * それぞれのクラスが削除される。
 */
classList.remove('ex2-1', 'ex2-2');
console.log(classList.toString());
// ex3-1 ex3-2 ex3-3


/**
 * 第三引数への指定も、有効に動作する。
 */
classList.remove('ex3-1', 'ex3-2', 'ex3-3');
console.log(classList.toString());
// 
```

引数にスペースが含まれる場合も、同じくエラーが発生します。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1-1', 'ex1-2', 'ex2-1', 'ex3-1');
console.log(classList.toString());
// ex1-1 ex1-2 ex2-1 ex3-1


/**
 * スペース区切りのクラス指定を試みる。
 * エラーとなり、クラスの削除は行われない。
 */
try {
    classList.remove('ex1-1 ex1-2');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'remove' on 'DOMTokenList': The token provided ('ex1-1 ex1-2') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// ex1-1 ex1-2 ex2-1 ex3-1


/**
 * 指定値の末端にスペースを追加。
 * これもエラーとなり、クラスの削除は行われない。
 */
try {
    classList.remove('ex2-1 ');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'remove' on 'DOMTokenList': The token provided ('ex2-1 ') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// ex1-1 ex1-2 ex2-1 ex3-1


/**
 * 指定値の先頭にスペースを追加した場合も同様。
 */
try {
    classList.remove(' ex3-1');
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'remove' on 'DOMTokenList': The token provided (' ex3-1') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// ex1-1 ex1-2 ex2-1 ex3-1
```

### classList.contains

`classList.contains`メソッドの場合、第二引数以降は無視されるようです。
判定は第一引数に指定されたクラスに対してのみ行われます。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1');
console.log(classList.toString());
// ex1


/**
 * クラス"ex1"を含むか検証。
 * 正しくtrueが出力される。
 */
console.log(
    classList.contains('ex1')
);
// true


/**
 * クラス"ex1"と"ex2"を含むか検証。
 * "ex1"は含まれるため、trueが出力されるようだが……。
 */
console.log(
    classList.contains('ex1', 'ex2')
);
// true


/**
 * クラス"ex1"と"ex2"を含むか検証。
 * "ex1"は含まれるものの、第一引数に指定されているのは"ex2"であるため、
 * falseが出力される。
 */
console.log(
    classList.contains('ex2', 'ex1')
);
// false


/**
 * クラス"ex2"と"ex3"を含むか検証。
 * どちらも含まれない……というより第一引数の"ex2"が含まれないため、
 * falseが出力される。
 */
console.log(
    classList.contains('ex2', 'ex3')
);
// false
```

スペースの含まれる引数指定時も、同様にエラーが発生します。
ただ、第二引数は無視されるため、第二引数以降にスペースが含まれる場合は挙動は変わりません。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1');
console.log(classList.toString());
// ex1


/**
 * 指定値の先頭にスペースを追加してクラス"ex1"を含むか検証。
 * エラーとなる。
 */
try {
    console.log(
        classList.contains(' ex1')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided (' ex1') contains HTML space characters, which are not valid in tokens.


/**
 * 指定値の末端にスペースを追加した場合も同様。
 */
try {
    console.log(
        classList.contains('ex1 ')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided ('ex1 ') contains HTML space characters, which are not valid in tokens.


/**
 * 含まないクラスも動作は同じ。
 */
try {
    console.log(
        classList.contains(' ex2')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided (' ex2') contains HTML space characters, which are not valid in tokens.


/**
 * 〃
 */
try {
    console.log(
        classList.contains('ex2 ')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided ('ex2 ') contains HTML space characters, which are not valid in tokens.


/**
 * クラス"ex1"と"ex2"を含むかスペース区切りで検証。
 * これもエラーとなり、判定不能。
 */
try {
    console.log(
        classList.contains('ex1 ex2')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided ('ex1 ex2') contains HTML space characters, which are not valid in tokens.


/**
 * 同上。
 */
try {
    console.log(
        classList.contains('ex2 ex1')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided ('ex2 ex1') contains HTML space characters, which are not valid in tokens.


/**
 * 仝
 */
try {
    console.log(
        classList.contains('ex2 ex3')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'contains' on 'DOMTokenList': The token provided ('ex2 ex3') contains HTML space characters, which are not valid in tokens.


/**
 * クラス"ex1"を含むか検証。
 * 正しくtrueが出力される。
 * 第二引数の誤った指定は無視される。
 */
try {
    console.log(
        classList.contains('ex1', ' ex1')
    );
} catch (e) {
    console.error(e.message);
}
// true


/**
 * クラス"ex2"を含むか検証。
 * 正しくfalseが出力される。
 * 第二引数の指定は同じく無視される。
 */
try {
    console.log(
        classList.contains('ex2', ' ex1')
    );
} catch (e) {
    console.error(e.message);
}
// false
```

### classList.toggle

`classList.toggle`メソッドは第一引数に指定したクラスが要素に含まれていれば削除し、含まれていなければ追加するメソッドです。
これに第二引数を指定すると、第二引数が`true`の場合は追加し、`false`の場合は削除するよう挙動が変わります。
第二引数にBoolean以外が指定された場合は、Booleanとして評価され動作するようです。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1', 'ex4');
console.log(classList.toString());
// ex1 ex4


/**
 * クラス"ex1"を含んでいれば削除する。
 * 正しくfalseが出力され、クラスも削除される。
 */
console.log(
    classList.toggle('ex1')
);
// false
console.log(classList.toString());
// ex4


/**
 * クラス"ex2"を含んでいなければ追加する。
 * 正しくtrueが出力され、クラスも追加される。
 */
console.log(
    classList.toggle('ex2')
);
// true
console.log(classList.toString());
// ex4 ex2


/**
 * 第二引数にtrueを指定し、
 * クラス"ex3"を強制的に追加する。
 * 正しくtrueが出力され、クラスも追加される。
 */
console.log(
    classList.toggle('ex3', true)
);
// true
console.log(classList.toString());
// ex4 ex2 ex3


/**
 * 連続して実行した場合も結果は同じ。
 */
console.log(
    classList.toggle('ex3', true)
);
// true
console.log(classList.toString());
// ex4 ex2 ex3


/**
 * 第二引数にfalseを指定し、
 * クラス"ex4"を強制的に削除する。
 * 正しくfalseが出力され、クラスも削除される。
 */
console.log(
    classList.toggle('ex4', false)
);
// false
console.log(classList.toString());
// ex2 ex3


/**
 * 連続して実行した場合も結果は同じ。
 */
console.log(
    classList.toggle('ex4', false)
);
// false
console.log(classList.toString());
// ex2 ex3
```

第一引数の値にスペースを含めた場合、他のメソッドと同じくエラーとなります。

```js
/**
 * classList API確認用の要素を作成し、
 * classListを変数に格納する。
 */
var element = document.createElement('div');
var classList = element.classList;


/**
 * 初期状態のクラス指定値を設定する。
 */
classList.add('ex1');
console.log(classList.toString());
// ex1


/**
 * 指定値にスペースが含まれる場合、
 * 他のメソッドと同じくこれも動作しない。
 */
try {
    console.log(
        classList.toggle('ex1 ')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'toggle' on 'DOMTokenList': The token provided ('ex1 ') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// ex1


/**
 * 〃
 */
try {
    console.log(
        classList.toggle(' ex2')
    );
} catch (e) {
    console.error(e.message);
}
// Failed to execute 'toggle' on 'DOMTokenList': The token provided (' ex2') contains HTML space characters, which are not valid in tokens.
console.log(classList.toString());
// ex1


/**
 * 第二引数の値はBooleanとして評価されるため、
 * クラス"ex3"を強制的に追加するよう動作する。
 * 正しくtrueが出力され、クラスも追加される。
 */
try {
    console.log(
        classList.toggle('ex3', 'ex4 ')
    );
} catch (e) {
    console.error(e.message);
}
// true
console.log(classList.toString());
// ex1 ex3
```

## 検証結果

* いずれのメソッドも**スペース区切りのクラス指定は受け付けない**。スペースが入るだけでもエラーとなる。
* `classList.add`と`classList.remove`は複数クラスの操作が可能。その際は複数の引数として指定する。スペース区切りは不可。
* `classList.contains`は単一クラスの判定のみ可能。第二引数以降は無視。
* `classList.toggle`は第二引数の利用で`classList.add`と`classList.remove`の代用が可能。複数のクラスを指定できない点を除き、`jQuery.toggleClass`とほぼ同等。

## 追記

~~よく読んだら[MDN](https://developer.mozilla.org/ja/docs/DOM/element.classList)の[JavaScript shim](https://developer.mozilla.org/ja/docs/DOM/element.classList#wrapper)がこの検証結果と合致するコードになっていました。
多分、これがW3Cの仕様なのでしょう…~~

<ins>仕様でそうなっているようです。</ins>

[DOM Standard](https://dom.spec.whatwg.org/#interface-domtokenlist)