class を付け外しする

このレッスンで分かること

  • classListadd remove toggle contains の使い分け
  • className への代入と何が違うのか
  • 見た目は CSS に任せ、JavaScript は class だけを切り替える考え方

class を付け外しする とは

要素の class を JavaScript から足したり外したりする操作です。CSS に用意しておいた見た目を、必要なときだけ当てられるようになります。

見た目を JavaScript で直接いじらない

作品カードを 1 枚だけ目立たせたい、としましょう。次のように書けば確かに色は変わります。

const card = document.querySelector(".work-card"); card.style.borderColor = "#0071e3"; card.style.backgroundColor = "#eef4fb";

動きますが、この書き方は増えるほど苦しくなります。強調の見た目を変えたくなったとき、CSS ファイルではなく JavaScript を直しに行くことになるからです。色や余白がどこに書いてあるのか分からなくなり、CSS ファイルを読んでも本当の見た目が分かりません。

そこで、見た目は CSS に書いておき、JavaScript は class を付け外しするだけにします。今回のプロフィールサイトの CSS には、次の指定を先に足してあります。

/* style.css に用意ずみ */ .work-card.featured { border-color: #0071e3; background-color: #eef4fb; }

あとは、強調したいカードに featured を付けるだけです。見た目を変えたくなったら CSS だけを直せばよく、JavaScript は触らずに済みます。役割がきれいに分かれます。

この分け方は、規模が大きくなるほど効いてきます。実務のフロントエンドでは「状態を class で表し、見え方は CSS が決める」という書き方がほぼ標準です。第9章でつくるダークモード切替も、bodydark を付け外しするだけで成り立ちます。

classList の 4 つの操作

要素の classList は、その要素が持つ class を扱うための入れ物です。よく使うのは次の 4 つです。

書き方すること返るもの
el.classList.add("x")x を足す。もうあれば何もしないなし
el.classList.remove("x")x を外す。無ければ何もしないなし
el.classList.toggle("x")あれば外し、無ければ足す足したなら true
el.classList.contains("x")x が付いているかを調べるtrue / false

どれも安全に作られています。すでに付いているものを add しても二重にはなりませんし、付いていないものを remove してもエラーにはなりません。だから「いまどうなっているか」を確かめずに呼べます。

const card = document.querySelector(".work-card"); card.classList.add("featured"); // class="work-card featured" card.classList.add("featured"); // 何も起きない。二重にならない card.classList.remove("featured"); // class="work-card" card.classList.remove("featured"); // 何も起きない。エラーも出ない

toggle は 1 行で切り替えができます。押すたびに表示を入れ替えるボタンは、これ 1 つで書けます。

card.classList.toggle("featured"); // 無かったので付いた card.classList.toggle("featured"); // あったので外れた

containstruefalse を返すので、そのまま if の条件に置けます。

if (card.classList.contains("featured")) { console.log("強調ずみです"); }

addremove は、まとめて複数を渡すこともできます。card.classList.add("featured", "new") のように書けば 2 つ同時に付きます。

className との違い

class を扱う方法はもう 1 つあります。className に文字列を代入するやり方です。

card.className = "work-card featured";

これは class 属性をまるごと差し替えます。いま付いている class が何であれ、書いた文字列に置きかわります。だから、他の場所で付けた class を知らないうちに消してしまいます。

// もともと class="work-card featured" だったとする card.className = "highlight"; // work-card も featured も消えた

比べると、次のような使い分けになります。

目的使うもの
1 つだけ足す・外す・切り替えるclassList
いまの class を全部捨てて置き直すclassName
新しくつくった要素に最初の class を付けるclassName でも可

つまり、すでに画面にある要素をいじるときは classList、これから自分でつくる要素の初期値を決めるときは className が向いています。次の回で createElement を使うときは className が出てきます。

classList は配列のようにも見えますが、lengthitem() を持つ専用のオブジェクトです。card.classList[0] で 1 つ目の class 名は取れますが、push はできません。足すときは add を使います。

今回書くこと

前回までのコードの下に、次の 3 つを足します。

  1. cards[0] (1 枚目の作品カード) に featured を付ける
  2. cards[2] (3 枚目) にいったん featured を付け、そのあと remove で外す
  3. contains で 1 枚目の状態を調べ、その結果を data-featured 属性に "yes""no" で入れる

cards は前々回に querySelectorAll(".work-card") で掴んでおいた NodeList です。番号で取り出せるので、cards[0]cards[2] がそのまま 1 枚目と 3 枚目になります。

3 番目は、第3章で練習した三項演算子の出番です。contains が返す true / false を、そのまま属性の文字列に変えます。

const isFeatured = firstCard.classList.contains("featured"); firstCard.setAttribute("data-featured", isFeatured ? "yes" : "no");

プレビューでは、1 枚目のカードだけ枠が青くなり、背景がうすい水色になります。3 枚目は付けてすぐ外したので、見た目は変わりません。

よくある間違い

class 名に . を付けてしまう

CSS のセレクタと混ざりやすいところです。classList に渡すのは class の名前だけで、. は要りません。

card.classList.add(".featured"); // 「.featured」という名前の class が付く card.classList.add("featured"); // 正しい

スペース区切りで 2 つ渡す

add"featured new" のような空白入りの文字列を渡すと InvalidCharacterError になります。2 つ足したいときは引数を 2 つに分けます。

card.classList.add("featured new"); // エラー card.classList.add("featured", "new"); // 正しい

CSS に無い class を付けて「効かない」と悩む

classList.add は、CSS にその指定があるかどうかを気にしません。存在しない class を付けても、エラーは出ず、見た目も変わりません。効かないときは、まず検証ツールで class が付いているかを見て、付いていたら CSS のほうに指定があるかを確かめます。

課題

  1. cards[0]classList.addfeatured を付ける
  2. cards[2]featured を付けたあと、classList.remove で外す
  3. cards[0]featured が付いているかを classList.contains で調べる
  4. 調べた結果を三項演算子で "yes""no" に変え、cards[0]data-featured 属性に入れる

ヒント

index.html
style.css
script.js
プレビュー

できているか

  • 1 枚目に featured が付く
  • 3 枚目の featured は外れている
  • contains の結果が data-featured に入る
  • 作品カードは 3 枚のまま