JavaScriptでHTML要素の見た目や状態を変えるときは、主にclassListとstyleを使います。
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にはpxやremなどを含む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引数はありません。autoやnormalなど数値でない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は、可能ならtransformやopacityを使い、毎frameのlayout測定を避けます。まず実測し、必要な箇所だけ最適化してください。
よくあるエラー
Cannot read properties of nullが出る
selectorに一致する要素がない状態でclassListやstyleを読んでいます。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へ分けると、変更しやすく崩れにくい実装になります。