class を付け外しする
このレッスンで分かること
classListのaddremovetogglecontainsの使い分け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章でつくるダークモード切替も、
bodyにdarkを付け外しするだけで成り立ちます。
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"); // あったので外れたcontains は true か false を返すので、そのまま if の条件に置けます。
if (card.classList.contains("featured")) {
console.log("強調ずみです");
}add と remove は、まとめて複数を渡すこともできます。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は配列のようにも見えますが、lengthとitem()を持つ専用のオブジェクトです。card.classList[0]で 1 つ目の class 名は取れますが、pushはできません。足すときはaddを使います。
今回書くこと
前回までのコードの下に、次の 3 つを足します。
cards[0](1 枚目の作品カード) にfeaturedを付けるcards[2](3 枚目) にいったんfeaturedを付け、そのあとremoveで外す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 のほうに指定があるかを確かめます。
課題
cards[0]にclassList.addでfeaturedを付けるcards[2]にfeaturedを付けたあと、classList.removeで外すcards[0]にfeaturedが付いているかをclassList.containsで調べる- 調べた結果を三項演算子で
"yes"か"no"に変え、cards[0]のdata-featured属性に入れる