jQueryの.removeClass()で「特定の文字列で始まるclass」をすべて削除する

jQuery の .removeClass() メソッドでは、「引数に関数を指定する」ことで柔軟なclass削除が実現できます。

# 「特定の文字列で始まるclass」を一括削除

「ユーザー操作に応じて画面表示を変更する」際など、要素の状態をあらわす "is-active"、"is-visible"、"is-fixed"、"is-closed" といった「特定の文字列で始まるclass」を複数利用する場合があると思います。

例えばこれらの "is-\*\*\*" というclassのみを一括で削除したい場合、正規表現を利用した以下の方法で実現できます。

~~~html:HTML
<div id="sample" class="block element is-active is-visible is-fixed">＊＊＊</div>

<script>
// 'is-' で始まるclassをすべて削除
$('#sample').removeClass(function(index, className) {
	return (className.match(/\bis-\S+/g) || []).join(' ');
});

// 結果： <div id="sample" class="block element">＊＊＊</div>
</script>
~~~

## もう少し詳しい説明

### 関数の戻り値が削除対象classとなる

.removeClass() メソッドの引数に関数が指定された場合、**その関数の戻り値（returnされた値）と一致するclass**が削除対象となります。

このことは、以下のコードの実行結果から確認できます。

~~~html:HTML
<div id="sample" class="block element is-active is-visible is-fixed">＊＊＊</div>

<script>
// 'is-active' を削除
$('#sample').removeClass(function() {
	return 'is-active';
});

// 結果： <div id="sample" class="block element is-visible is-fixed">＊＊＊</div>
</script>
~~~

つまり
**「関数をうまく利用して、削除したいclassをきちんとreturnできれば、複雑な処理も実現できそう」**
ということになりますね。

### 関数で利用できる2つの引数

この関数では2つの引数を利用することができ、

- 1つめの引数には「対象要素のインデックス番号」
- 2つめの引数には「対象要素の現在のclass属性値」

が入ります。

元のコードに戻り、
console.log() で確認してみましょう。

~~~html:HTML
<div id="sample" class="block element is-active is-visible is-fixed">＊＊＊</div>

<script>
// 'is-' で始まるclassをすべて削除
$('#sample').removeClass(function(index, className) {

	console.log(index); // 0
	console.log(className); // 'block element is-active is-visible is-fixed'

	return (className.match(/\bis-\S+/g) || []).join(' ');
});

// 結果： <div id="sample" class="block element">＊＊＊</div>
</script>
~~~

$('#sample') のインデックス番号「0」と、
現在のclass属性値「'block element is-active is-visible is-fixed'」が出力されることを確認できました。

### 正規表現を利用したマッチング

続いて、return されている値の部分に注目します。

~~~javascript
(className.match(/\bis-\S+/g) || []).join(' ')
~~~

ここでの処理の流れとしては、

1. 現在のclass属性値の文字列（'block element is-active is-visible is-fixed'）に対して.match() メソッドを実行し、正規表現（/\bis-\S+/）にマッチする値すべてを配列として取得
2. 上記の配列に対して.join() メソッドを実行し、半角スペース区切りで結合して文字列に変換

となります。

正規表現の部分では「特殊文字」の使い方がポイントになります。
今回使用する特殊文字は以下です。

\b：単語の区切りにマッチ
\S：ホワイトスペース以外の1文字にマッチ
+：直前の文字の1回以上の繰り返しにマッチ

つまり /\bis-\S+/ は
「{単語の区切り}is-{ホワイトスペース以外の1文字以上}」
にマッチすることになります。

末尾のgは「グローバル検索フラグ」です。
これが無い場合は最初のマッチ（'is-active'）の時点で検索処理が終了してしまうため、
忘れずに付けるようにしましょう。

### 例外（エラー）処理も重要

.match() メソッドの直後に現れる

~~~javascript
|| []
~~~

は何を意味するのでしょうか。

これは、「正規表現にマッチするclassが1つも存在しなかった場合」に
後続の.join() メソッドでエラーが発生してしまうのを防ぐための処理です。

以下の例のように、"is-" で始まるclassを1つも持たない要素を対象とした場合、3つめの console.log() でエラーが発生することが確認できます。

~~~html:HTML
<div id="sample" class="block element">＊＊＊</div>

<script>
// 'is-' で始まるclassをすべて削除
$('#sample').removeClass(function(index, className) {

	console.log(className); // 'block element'
	console.log(className.match(/\bis-\S+/g)); // null
	console.log((className.match(/\bis-\S+/g)).join(' ')); // Uncaught TypeError: Cannot read property 'join' of null

});
</script>
~~~

null に対して.join() メソッドなんて使えないよ、ということですね。

~~~javascript
|| []
~~~

を追記することで、マッチするclassが無かった場合には空の配列が生成され、
.join() メソッド呼び出し時のエラーを回避できます。

### まとめ

ここまでの内容をふまえ、
コードを小さな単位に分解して console.log() してみましょう。
処理の流れがより理解できるかと思います。

~~~html:HTML
<div id="sample" class="block element is-active is-visible is-fixed">＊＊＊</div>

<script>
// 'is-' で始まるclassをすべて削除
$('#sample').removeClass(function(index, className) {

	console.log(className); // 'block element is-active is-visible is-fixed'
	console.log(className.match(/\bis-\S+/g)); // ['is-active', 'is-visible', 'is-fixed']
	console.log((className.match(/\bis-\S+/g) || []).join(' ')); // 'is-active is-visible is-fixed'

	return (className.match(/\bis-\S+/g) || []).join(' ');
	// つまり
	// return 'is-active is-visible is-fixed';
});

// 結果： <div id="sample" class="block element">＊＊＊</div>
</script>
~~~

期待通りに 'is-active is-visible is-fixed' が return され、
**「"is-\*\*\*" というclassのみを一括で削除」**が実現できました。

# .addClass() は？

何となく想像はつくかと思いますが、
.addClass() メソッドでも同様に、引数に関数を指定することができます。

~~~html:HTML
<ul id="sampleList">
  <li>＊＊＊</li>
  <li>＊＊＊</li>
</ul>

<script>
// 'li' に連番でclassを追加
$('#sampleList li').addClass(function(index, className) {
    return 'item-' + index;
});

// 結果：
// <ul id="sampleList">
//   <li class="item-0">＊＊＊</li>
//   <li class="item-1">＊＊＊</li>
// </ul>
</script>
~~~

# 参考リンク

より詳しい内容については公式リファレンスを参照してください。

[.removeClass() | jQuery API Documentation](https://api.jquery.com/removeclass/)
[.addClass() | jQuery API Documentation](https://api.jquery.com/addclass/)