Web制作で「ボタンをクリックしたらメニューを開閉したい」「モーダルを表示・非表示にしたい」といったUIを実装するとき、JavaScriptで頻繁に使うのがclassListです。
要素の見た目を変えたいとき、JavaScriptからtarget.style.display = 'none'のようにスタイルを直接書き換えることもできます。
しかし実務では、「見た目のデザインはCSS、状態の切り替えはJavaScript」と役割を分担し、クラスの付け外しによって表示を切り替える設計が基本です。
この記事では、classListの基本操作であるadd()、remove()、toggle()の使い分けから、表示・非表示を切り替える実践コード、動かないときのトラブルシューティングまでを分かりやすく解説します。
classListでまず覚える3つ
classListを使ったクラス操作は、以下の3つのメソッドを押さえておけば、多くのUIで使う基本的な状態切り替えに対応できます。
add():クラスを追加する(開く、表示する)remove():クラスを削除する(閉じる、非表示にする)toggle():クラスが付いていれば削除し、付いていなければ追加する(反転・開閉)
const element = document.querySelector('.js-target');
element.classList.add('is-open'); // クラスを付ける
element.classList.remove('is-open'); // クラスを外す
element.classList.toggle('is-open'); // クラスを付け外しする
「確定した状態へ切り替えたいならadd / remove」「クリックするたびに開閉を繰り返すならtoggle」と覚えると整理しやすくなります。
classListとは?
classListは、HTML要素のclass属性をDOMTokenListとして取得し、クラス名を追加・削除・確認するためのプロパティです。
<div class="box js-box">コンテンツ</div>
const box = document.querySelector('.js-box');
console.log(box.classList);
// DOMTokenList(2) ["box", "js-box"]
classListから取得したDOMTokenListは要素のclass属性と連動しているため、add()やremove()を実行すると実際のHTMLのクラスも更新されます。
既存のクラスを壊さずに安全に追加・削除が行えるのが大きな特徴です。
classList.add()でクラスを追加する
要素に新しいクラスを付けたいときはclassList.add()を使います。
const box = document.querySelector('.js-box');
box.classList.add('is-active');
実行後のHTML:
<div class="box js-box is-active">コンテンツ</div>
すでに同じクラスが付いている要素に対してadd()を実行しても、クラスが重複して増えることはありません。
【注意】クラス名に「.」は付けない
初心者が非常につまずきやすいポイントが、引数のクラス名にドット(.)を付けてしまうミスです。
// ✕ 通常意図する書き方ではない
box.classList.add('.is-active');
// → ".is-active" というクラス名そのものが追加される
// ◯ クラス名だけを渡す
box.classList.add('is-active');
document.querySelector('.is-active')ではCSSセレクタとして指定するため先頭の.が必要ですが、classList.add()にはクラス名そのものを渡すため.は付けません。
'.is-active'とすると、ドットを含んだ別のクラス名が追加されるため、通常の.is-active { ... }というCSSルールとは一致しなくなります。
classList.remove()でクラスを削除する
特定のクラスを取り除きたいときはclassList.remove()を使います。
box.classList.remove('is-active');
削除対象として指定したクラスがその要素に最初から付いていなかった場合でも、エラーは発生せず処理はそのまま続行されます。
classList.toggle()で付け外しする
メニューの開閉(ハンバーガーメニュー)やアコーディオンのように、1つのボタンを押すたびに「表示」と「非表示」を交互に切り替えたい場合はclassList.toggle()が適しています。
button.addEventListener('click', () => {
box.classList.toggle('is-open');
});
- 1回目のクリック:
is-openが付いていないため追加される(開く) - 2回目のクリック:
is-openが付いているため削除される(閉じる)
手動で「現在クラスが付いているかどうか」をif文で判定する必要がなく、1行で状態を反転できます。
add・remove・toggleはどう使い分ける?
3つのメソッドの動作と使い分けの基準です。
| メソッド | 動作 | 主な使いどころ |
|---|---|---|
add() |
指定クラスを必ず追加する | 「モーダルを開く」「タブを選択状態にする」など、一方通行の有効化 |
remove() |
指定クラスを必ず削除する | 「閉じるボタン」「リセット」など、一方通行の解除 |
toggle() |
指定クラスの有無を反転させる | 「メニューの開閉ボタン」「表示/非表示ボタン」など、交互に切り替える操作 |
「閉じるボタンを押したときは、確実に閉じたい(すでに閉じていても問題ない)」という場面でtoggle()を使うと、二重クリックで誤って再び開いてしまう原因になります。
最終的な状態を固定したい場合はadd / remove、クリックで交互に状態を変えたい場合はtoggleを選ぶのが基本です。
classListで表示・非表示を切り替える実践コード
querySelector、addEventListener、classList.toggleを組み合わせた最小の表示切り替え実装です。
<button type="button" class="js-toggle">表示を切り替える</button>
<div class="box js-target">
表示・非表示を切り替えるコンテンツ
</div>
/* 非表示用の状態クラス */
.is-hidden {
display: none;
}
const button = document.querySelector('.js-toggle');
const target = document.querySelector('.js-target');
button.addEventListener('click', () => {
// クリックするたびに is-hidden クラスを付け外しする
target.classList.toggle('is-hidden');
});
ボタンを押すたびに.is-hiddenクラスが着脱され、CSSのdisplay: noneが適用・解除されることで表示が切り替わります。
toggle()の第2引数を使うと状態を明示できる
toggle()には、第2引数として真偽値(true / false)を渡すことができます。
// true を渡すと、付いていなければ追加する(強制 add)
element.classList.toggle('is-open', true);
// false を渡すと、付いていれば削除する(強制 remove)
element.classList.toggle('is-open', false);
この仕組みを利用すると、「条件を満たしているときだけクラスを付ける」といった処理を簡潔に書くことができます。
const shouldOpen = window.innerWidth > 768;
// 画面幅が768px超のときだけクラスを付け、768px以下のときは外す
menu.classList.toggle('is-open', shouldOpen);
classList.contains()でクラスが付いているか確認する
現在の要素に特定のクラスが含まれているかどうかを真偽値(true / false)で調べたいときは、classList.contains()を使用します。
if (menu.classList.contains('is-open')) {
console.log('メニューは現在開いています');
} else {
console.log('メニューは閉じています');
}
現在の状態に応じた条件分岐を行いたい場合に役立ちます。
classListが動かないときによくある原因
「コードを書いたのにクラスが付かない」「見た目が変わらない」という場合によくある原因です。
1. classListの引数に「.」を付けている
// ✕
box.classList.add('.is-active');
前述の通り、ドットを付けると".is-active"というドットを含んだ別のクラス名として追加されるため、CSS側の.is-active { ... }とは一致しません。
2. CSS側にそのクラスのスタイルが定義されていない
JavaScriptでクラスの追加自体は成功していても、CSS側に.is-hidden { display: none; }などのスタイルが書かれていなければ、画面の見た目は一切変化しません。
3. 対象要素がnullになっている
const box = document.querySelector('.js-box');
box.classList.add('is-active');
// → TypeError: Cannot read properties of null (reading 'classList')
要素の取得に失敗していると、classListを呼び出した瞬間にスクリプトがエラーで止まります。
セレクタの指定ミスや<script>の実行タイミングを見直す必要があります。
参照:querySelectorで要素を取得できない・nullになる原因と確認方法
4. clickイベント自体が発火していない
ボタンをクリックしてもクラスが変わらない場合、classListの問題ではなく、手前のaddEventListenerが正しく動いていないケースがあります。
参照:addEventListenerでclickイベントが動かない原因と確認方法
5. 複数のクラスを1つの文字列でまとめて渡している
複数のクラスを一度に追加・削除したい場合、半角スペースで区切った1つの文字列を渡すとエラー(InvalidCharacterError)になります。
// ✕ エラーになる(スペースを含む文字列は渡せない)
box.classList.add('is-open is-active');
// ◯ 正しい記述(カンマ区切りで別々の引数として渡す)
box.classList.add('is-open', 'is-active');
// ◯ 削除も同様に複数指定可能
box.classList.remove('is-open', 'is-active');
classNameとの違い
昔のJavaScriptコードでは、要素のクラスをelement.classNameで変更している例も見られます。
// className を使う場合
box.className = 'is-open';
classNameはクラス属性全体を1つの文字列として丸ごと上書きします。
そのため、元々要素に付いていたboxやjs-boxといった他のクラスまで全て消去されてしまいます。
一方、classList.add()であれば、既存のクラスをそのまま維持しながら特定のクラスだけを追加できます。
実務でクラスを着脱する際は、予期せぬクラス消失を防ぐためにも基本的にclassListを使用します。
JavaScriptでstyleを直接変更する方法との違い
表示・非表示の切り替えは、以下のようにインラインスタイルを直接書き換えることでも実現できます。
target.style.display = 'none'; // 非表示
target.style.display = 'block'; // 表示
しかし、JavaScript内でスタイルを直接操作すると、以下のデメリットが生じます。
- CSS側から上書きしにくくなる:JavaScriptで設定したインラインスタイルは通常のCSSより優先されるため、メディアクエリなどCSS側のルールで切り替えにくくなる
- レイアウトを壊しやすい:表示時に
display = 'block'と決め打ちすると、本来display: flexやdisplay: gridで表示していた要素のレイアウトを壊すことがある(状態クラスを着脱する形なら、表示時の本来のCSSをそのまま利用できる) - 状態管理が複雑になる:「開いている」「選択中」「エラー表示」など状態が増えた際、JS側で大量のCSSプロパティを管理する必要が出る
「UIの見た目やレイアウト定義はCSSに集約し、JavaScriptは現在の状態(クラス)の付け外しだけに専念させる」設計にしておくことで、コードの見通しと保守性が大幅に向上します。
classListでうまく切り替わっているか確認する方法
意図通りに動かないときは、ブラウザの開発者ツール(DevTools)を使って「JavaScript側の問題」か「CSS側の問題」かを切り分けます。
1. Elementsパネルでclass属性の変化を直接見る
ブラウザの開発者ツール(F12)を開き、「Elements」パネルで対象の要素を選択した状態でボタンをクリックしてみます。
<!-- クリック前 -->
<div class="box js-target">
<!-- クリック後:クラスが増減していればJavaScript側は正常 -->
<div class="box js-target is-hidden">
クリック時にクラス名がリアルタイムで増減していれば、JavaScriptの取得・イベント登録・classList操作はすべて成功しています。
その場合は、CSSのセレクタ名や読み込み順、詳細度などCSS側を疑うのが正しいアプローチです。
2. Consoleでクラスの状態を出力する
button.addEventListener('click', () => {
target.classList.toggle('is-open');
console.log('現在のクラス一覧:', target.classList);
console.log('is-openが付いているか:', target.classList.contains('is-open'));
});
classListが動かないときの確認手順
トラブルが起きたときは、以下のステップで1つずつ確認してみてください。
- 要素の取得確認:
querySelectorで取得した変数がnullではないか - イベント発火の確認:ボタンクリック時に
addEventListener内の処理が動いているか(console.logを置いてみる) - 引数の記述確認:
classList.add('.class')のようにドットを付けていないか、複数クラスをスペース区切りで渡していないか - DevToolsでのクラス変化確認:Elementsパネルで、クリック時に実際のDOMの
class属性が変わっているか - CSS定義の確認:付与したクラス名に対応するCSSルールが正しく記述されているか
まとめ
classListは、JavaScriptからHTML要素の表示・非表示やUIの状態変化を扱うときに、よく使われる基本的なプロパティです。
- 基本の3メソッド:追加は
add()、削除はremove()、交互の反転はtoggle() - 指定のルール:セレクタではないため、クラス名に先頭の
.は付けない - 設計の原則:
style.displayを直接書き換えるのではなく、CSSでスタイルを定義してclassListで着脱する - デバッグのコツ:Elementsパネルでクラスが増減しているかを確認し、JSの問題かCSSの問題かを切り分ける
querySelectorで対象を見つけ、addEventListenerで操作を拾い、classListで状態を切り替える。
この3ステップを押さえておくと、メニューの開閉や表示・非表示、選択状態の切り替えなど、Web制作でよく使うUIを実装するための基本が身につきます。