JavaScript

JavaScriptでclass・styleを操作する方法|classListとgetComputedStyle

JavaScriptでHTML要素のclassとstyleを操作する方法を解説します。classListのadd・remove・toggle・contains・replace、style、setProperty、CSS変数、getComputedStyle、classNameとの違い、アクセシビリティとパフォーマンスまで実例で確認できます。

PR

この記事の目次
  1. classListでclass属性を操作する
  2. classList.add()でclassを追加する
  3. classList.remove()でclassを削除する
  4. classList.toggle()でclassを切り替える
  5. 第二引数forceで状態を明示する
  6. classList.contains()でclassの有無を確認する
  7. classList.replace()でclassを置き換える
  8. classNameとの違い
  9. style propertyでinline styleを設定する
  10. element.styleで取得できるのはinline style
  11. inline styleを削除する
  12. setProperty()でCSS propertyを設定する
  13. JavaScriptからCSS変数を変更する
  14. getComputedStyle()で適用後のstyleを取得する
  15. 数値として計算する
  16. pseudo-elementのstyleを読む
  17. classとstyleの使い分け
  18. 実例:開閉panelのclassとARIAを同期する
  19. performance:styleの読み書きをまとめる
  20. よくあるエラー
  21. Cannot read properties of nullが出る
  22. classを追加しても見た目が変わらない
  23. element.style.colorが空文字になる
  24. styleへwidth = 100と書いても反映されない
  25. toggleの結果が逆になる
  26. 書籍でJavaScriptとDOMを体系的に学びたい人へ
  27. まとめ

JavaScriptでHTML要素の見た目や状態を変えるときは、主にclassListstyleを使います。

const panel = document.querySelector(".panel");

if (panel) {
  panel.classList.add("is-open");
  panel.style.setProperty("--panel-height", "320px");
}

基本方針は次のとおりです。

  • 複数のCSS宣言を状態ごとに切り替える: classList
  • 計算した座標・寸法やCSS変数を要素ごとに渡す: style
  • stylesheetや継承を含む適用後の値を調べる: getComputedStyle()

JavaScriptに大量の色・余白・layoutを書くより、CSSへ見た目を置き、JavaScriptは状態classを切り替える構成が保守しやすくなります。

classListでclass属性を操作する

element.classListは、要素のclass属性をspace区切りの文字列ではなく、liveなDOMTokenListとして扱います。

<button class="button button--primary">保存</button>
const button = document.querySelector(".button");

if (button) {
  console.log(button.classList.length); // 2
  console.log(button.classList.contains("button--primary")); // true
}

classList自体はread-only propertyですが、返されたlistをadd()remove()で変更できます。

classList.add()でclassを追加する

const message = document.querySelector(".message");

if (message) {
  message.classList.add("is-visible");
}

同じclassが既に存在しても重複しません。複数classを一度に追加できます。

message?.classList.add("is-visible", "has-animation");

const classes = ["theme-dark", "is-compact"];
message?.classList.add(...classes);

1つの引数へ"is-visible has-animation"とspace付き文字列を渡すのではなく、tokenごとに引数を分けます。

classList.remove()でclassを削除する

message?.classList.remove("is-visible");
message?.classList.remove("is-visible", "has-animation");

存在しないclassを削除してもerrorにはなりません。元のclassを残したまま、指定した状態classだけを外せます。

classList.toggle()でclassを切り替える

1引数のtoggle()は、classがあれば削除し、なければ追加します。戻り値は操作後にclassが存在すればtrueです。

const isOpen = panel?.classList.toggle("is-open");
console.log(isOpen);

第二引数forceで状態を明示する

booleanの状態が既にあるなら、2番目のforce引数を使います。

panel?.classList.toggle("is-open", shouldOpen);

shouldOpenがtrueなら追加、falseなら削除します。eventが重複しても意図した状態へ揃えられるため、単純なtoggleより安全な場面があります。

classList.contains()でclassの有無を確認する

if (panel?.classList.contains("is-open")) {
  console.log("panelは開いています");
}

contains()はbooleanを返します。=== trueを追加する必要はありません。

classList.replace()でclassを置き換える

const replaced = message?.classList.replace(
  "message--info",
  "message--success",
);

console.log(replaced);

置換元が存在すればtrue、存在しなければfalseを返し、新しいclassも追加しません。themeやstatusのmutually exclusiveなclassを交換するときに使えます。

classNameとの違い

classNameはclass属性全体を文字列として取得・置換します。

const button = document.querySelector(".button");

if (button) {
  console.log(button.className); // "button button--primary"
  button.className = "button button--danger";
}

代入すると既存classをまとめて上書きします。1つだけ追加・削除したい場合はclassListが適しています。

classがJavaScriptのkeywordであることは、DOM APIがclassNameという名前になった歴史的背景の一部ですが、「classを選択するにはclassNameを使う」という意味ではありません。selectorには通常document.querySelector(".class-name")を使います。

style propertyでinline styleを設定する

element.styleは、要素のinline styleをliveなCSSStylePropertiesとして返します。

const box = document.querySelector(".box");

if (box) {
  box.style.backgroundColor = "#37383d";
  box.style.width = "240px";
  box.style.transform = "translateX(20px)";
}

hyphenを含むCSS propertyはcamelCaseで書けます。

CSS JavaScript
background-color style.backgroundColor
font-size style.fontSize
z-index style.zIndex

numberだけを代入しても単位が補われるとは限りません。lengthにはpxremなどを含むstringを渡します。

element.styleで取得できるのはinline style

次のCSSがstylesheetにあっても、inline styleがなければbox.style.heightは空文字です。

.box {
  height: 80px;
}
console.log(box.style.height); // ""

style objectにはbrowserが対応するproperty名が用意されていますが、値として取得できるのはその要素のinline styleです。stylesheet、inheritance、cascadeを反映した値はgetComputedStyle()で読みます。

inline styleを削除する

個別propertyへ空文字を代入するとinline指定を解除できます。

box.style.width = "";

hyphen-caseのproperty名ならremoveProperty()も使えます。戻り値は削除前の値です。

const previousValue = box.style.removeProperty("background-color");
console.log(previousValue);

削除すると、stylesheetのruleが再びcascadeで適用されます。

setProperty()でCSS propertyを設定する

setProperty(propertyName, value, priority)ではproperty名をhyphen-caseで指定します。

box.style.setProperty("background-color", "tomato");
box.style.setProperty("width", "240px");

!importantが必要な場合、valueへ含めず3番目の引数を使います。

box.style.setProperty("display", "block", "important");

ただし、JavaScriptからpriorityを強制する前にCSSのspecificityや設計を見直してください。

JavaScriptからCSS変数を変更する

CSS custom propertyはsetProperty()で操作します。

:root {
  --color-primary: #2563eb;
}

.button {
  background: var(--color-primary);
}

page全体へ適用する例です。

document.documentElement.style.setProperty(
  "--color-primary",
  "#dc2626",
);

特定の要素だけへ設定すれば、その要素と継承先へscopeを限定できます。

panel?.style.setProperty("--panel-accent", "#16a34a");

inlineのcustom propertyを読むにはelement.style.getPropertyValue()、cascade後の値を読むにはgetComputedStyle()を使います。

const primaryColor = getComputedStyle(document.documentElement)
  .getPropertyValue("--color-primary")
  .trim();

CSS変数のscope・inheritance・fallbackはCSS変数の使い方で詳しく扱います。

getComputedStyle()で適用後のstyleを取得する

window.getComputedStyle(element)は、inline style、style element、external stylesheet、inheritance、cascadeを反映したresolved valueを持つlive・read-only objectを返します。

const box = document.querySelector(".box");

if (box) {
  const styles = window.getComputedStyle(box);
  console.log(styles.width);
  console.log(styles.getPropertyValue("background-color"));
}

layout依存propertyではused valueが返る場合があり、animation中なら現在時点の値になります。返されたobjectはread-onlyなので、変更にはelement.styleまたはclassを使います。

数値として計算する

getComputedStyle()の値は"80px"のようなstringです。

const currentHeight = Number.parseFloat(
  window.getComputedStyle(box).height,
);

if (Number.isFinite(currentHeight)) {
  box.style.height = `${currentHeight + 40}px`;
}

Number.parseFloat()にradix引数はありません。autonormalなど数値でないresolved valueもあるため、変換結果を確認します。

pseudo-elementのstyleを読む

2番目の引数へpseudo-element selectorを渡せます。

const beforeStyles = window.getComputedStyle(box, "::before");
console.log(beforeStyles.content);

pseudo-element自体をquerySelector()で取得することはできません。状態変更は親要素のclassやCSS変数を通して行います。

classとstyleの使い分け

状況 推奨
open・active・errorなど意味のある状態 classList
複数propertyをまとめて切り替える classList + CSS
drag位置、実測height、progressなど連続値 styleまたはCSS変数
適用後の値を診断・測定する getComputedStyle()

style.cssTextへ代入するとinline style全体を置き換えます。既存のinline指定を残したい場合は個別propertyを設定してください。外部入力をそのままcssTextへ渡す設計も避けます。

実例:開閉panelのclassとARIAを同期する

見た目だけでなく、buttonのaria-expandedとpanelのhiddenも同期します。

<button
  class="panel-button"
  type="button"
  aria-expanded="false"
  aria-controls="details-panel"
>
  詳細を表示
</button>

<div id="details-panel" class="panel" hidden>
  詳細内容
</div>
const button = document.querySelector(".panel-button");
const panel = document.querySelector("#details-panel");

button?.addEventListener("click", () => {
  if (!panel) return;

  const willOpen = button.getAttribute("aria-expanded") !== "true";

  button.setAttribute("aria-expanded", String(willOpen));
  panel.hidden = !willOpen;
  panel.classList.toggle("is-open", willOpen);
});

classはCSS selectorのための状態、hiddenは表示状態、aria-expandedはcontrolの展開状態を支援技術へ伝えます。classだけを切り替えて終わらせないことが重要です。

event登録の詳細はaddEventListenerの使い方を参照してください。

performance:styleの読み書きをまとめる

getComputedStyle()や寸法propertyを読む直前にstyleを書き換えると、browserが最新layoutを確定するため同期計算を行う場合があります。大量要素のloopでは読み取りと書き込みを分けます。

const items = [...document.querySelectorAll(".item")];

// 先に読む
const widths = items.map((item) => item.getBoundingClientRect().width);

// 後からまとめて書く
items.forEach((item, index) => {
  item.style.setProperty("--measured-width", `${widths[index]}px`);
});

頻繁に変わるanimationは、可能ならtransformopacityを使い、毎frameのlayout測定を避けます。まず実測し、必要な箇所だけ最適化してください。

よくあるエラー

Cannot read properties of nullが出る

selectorに一致する要素がない状態でclassListstyleを読んでいます。scriptの実行位置、selector、DOMContentLoadedを確認し、null checkを行います。

classを追加しても見た目が変わらない

CSSに対応ruleがあるか、selectorが一致するか、specificityで上書きされていないかをDevToolsで確認します。classList.contains()でDOM上の状態も確認できます。

element.style.colorが空文字になる

stylesheetから適用された値はelement.styleでは取得できません。getComputedStyle(element).colorを使います。

styleへwidth = 100と書いても反映されない

CSS lengthには単位が必要です。element.style.width = "100px"のように指定します。

toggleの結果が逆になる

別の処理も同じclassを変更している可能性があります。desired stateが分かっているならtoggle("is-open", shouldOpen)で明示します。

書籍でJavaScriptとDOMを体系的に学びたい人へ

この記事のclassやstyle操作だけでなく、JavaScriptの基礎とDOM操作を一冊の順番で学び直したい場合の購入候補です。ブラウザAPIや対応状況は変化するため、実装時はMDNや利用対象ブラウザの最新情報も確認してください。

価格・在庫・送料条件は変わるため、リンク先で最新情報をご確認ください。

まとめ

JavaScriptからclassとstyleを扱うときは、用途を分けます。

  • classの追加・削除・確認・置換はclassList
  • 既存class全体の置換だけclassName
  • inline styleの個別設定はelement.style
  • hyphen-caseやCSS変数はsetProperty()
  • inline指定の解除は空文字またはremoveProperty()
  • cascade後のresolved valueはgetComputedStyle()

UI状態はclassだけでなく、hiddenやARIA属性も同じsource of truthから同期します。見た目のruleはCSS、状態制御はJavaScriptへ分けると、変更しやすく崩れにくい実装になります。