この章では、JavaScriptの品質について学びます。
ここまでの章では、UIを動かす方法、フォームを扱う方法、アニメーションやスライダーを学んできました。
しかし、実務のJavaScriptは「動けばOK」ではありません。
重すぎないか、キーボードで操作できるか、スクリーンリーダーに状態が伝わるか、危険な値の扱いをしていないか。こうした視点も、納品物の品質に関わります。
パフォーマンスとは
パフォーマンスは、サイトの表示速度や操作の軽さに関わります。
JavaScriptが重いと、次のような問題が起きます。
- ページ表示が遅くなる
- ボタンを押しても反応が遅い
- スクロールがカクつく
- スマホで操作しづらい
- アニメーションが不自然になる
特にWeb制作では、画像やWebフォント、CSS、外部タグも重さに影響します。
JavaScriptだけ見ればよいわけではありませんが、JavaScriptが重くなりすぎないようにする意識は必要です。
JavaScriptを読み込みすぎない
ライブラリは便利ですが、増やせば増やすほど読み込み量が増えます。
main.js
gsap.js
scrolltrigger.js
splide.js
validation.js
map-widget.js
外部チャットタグ
予約ウィジェットタグ
SNS埋め込みタグ1つ1つは小さく見えても、積み重なると重くなります。
defer
JavaScriptの読み込み位置や実行タイミングも重要です。
<script src="/assets/js/main.js" defer></script>deferを付けると、HTMLの解析を妨げにくくなり、HTMLの解析後にJavaScriptが実行されます。
多くのWeb制作案件では、共通JavaScriptをdefer付きで読み込む構成が扱いやすいです。
DOM操作を減らす
DOM操作は便利ですが、何度も大量に行うと重くなることがあります。
避けたい例です。
items.forEach((item) => {
document.querySelector(".js-count").textContent = item.textContent;
});ループのたびに同じ要素を探しています。
先に取得しておくと読みやすくなります。
const countText = document.querySelector(".js-count");
items.forEach((item) => {
countText.textContent = item.textContent;
});実際にはこの例もロジックとしては不自然ですが、考え方としては「何度も同じDOM取得をしない」ことが大切です。
scrollイベントを重くしない
scrollイベントは、スクロール中に何度も発火します。
window.addEventListener("scroll", () => {
const isScrolled = window.scrollY > 100;
header.classList.toggle("is-scrolled", isScrolled);
});この程度なら問題になりにくいですが、スクロールのたびに大量の要素を取得したり、高さを計算したり、重い処理をするとカクつきます。
要素が画面に入ったかどうかを見たいだけなら、Intersection Observerを使う方が向いています。
アニメーションはtransformとopacity中心
アニメーションでは、できるだけtransformとopacityを中心に使います。
.fade-in {
opacity: 0;
transform: translateY(24px);
transition:
opacity 0.4s ease,
transform 0.4s ease;
}width、height、top、leftなどを頻繁に動かすと、レイアウト計算が増えやすくなります。
もちろん絶対に使ってはいけないわけではありませんが、基本はtransformとopacityを優先します。
Lighthouseで確認する
ChromeのLighthouseを使うと、パフォーマンスやアクセシビリティのヒントを確認できます。
ただし、スコアだけを追いかけすぎるのは危険です。
アクセシビリティとは
アクセシビリティは、できるだけ多くの人が情報や機能にアクセスできるようにする考え方です。
JavaScriptでUIを作る時は、マウスでクリックできるだけでは不十分です。
- キーボードで操作できるか
- フォーカス位置がわかるか
- 開閉状態が支援技術に伝わるか
- エラー状態が入力欄と関連付いているか
- 動きが苦手な人に配慮しているか
こうした点を確認します。
ボタンとリンクを正しく使う
クリックできるUIを作る時、何でもdivにしてはいけません。
<div class="js-menu-button">メニュー</div>見た目はボタンのようにできますが、キーボード操作や意味の面で不十分です。
<button class="js-menu-button" type="button">メニュー</button>ページ遷移するものはa、その場で状態を変えるものはbuttonを使います。
aria-expanded
aria-expandedは、開閉できる要素が開いているかどうかを表します。
<button class="js-accordion-button" type="button" aria-expanded="false">
質問
</button>const isOpen = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!isOpen));アコーディオン、メニュー、ドロップダウンなどで使います。
aria-hidden
aria-hiddenは、支援技術に対して要素を隠すかどうかを表します。
menu.setAttribute("aria-hidden", "true");ただし、見た目の表示非表示とは別です。
CSSやhidden属性と組み合わせて、実際の表示状態とも矛盾しないようにします。
aria-invalidとaria-describedby
フォームのエラーでは、aria-invalidとaria-describedbyをよく使います。
<input
id="email"
class="js-email-input"
type="email"
aria-describedby="email-error"
/>
<p id="email-error" class="js-email-error" hidden></p>emailInput.setAttribute("aria-invalid", "true");
emailError.hidden = false;
emailError.textContent = "メールアドレスを入力してください。";入力欄とエラーメッセージが関連付くようにします。
フォーカス管理
モーダルやメニューを開閉する時は、フォーカス位置も考えます。
function closeModal() {
modal.hidden = true;
openButton.focus();
}モーダルを閉じた後、フォーカスがどこかへ消えると、キーボード操作中のユーザーが迷います。
実務のモーダルでは、開いている間のフォーカストラップも検討します。
prefers-reduced-motion
動きが苦手なユーザーのために、アニメーションを減らす設定を考慮します。
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}JavaScript側で大きな演出を実行しないようにすることもできます。
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!prefersReducedMotion) {
// アニメーションを実行する
}セキュリティの入口
フロントエンドのJavaScriptでも、危険な書き方があります。
特に注意したいのは、ユーザー入力をHTMLとして扱うことです。
message.innerHTML = input.value;ユーザーが入力した文字列にHTMLやスクリプトが含まれていた場合、XSSと呼ばれる問題につながる可能性があります。
単純に文字として表示したいなら、textContentを使います。
message.textContent = input.value;APIキーをフロントに置かない
JavaScriptファイルは、ブラウザから見えます。
そのため、秘密にするべきAPIキーやトークンをフロント側に書いてはいけません。
const secretApiKey = "sk_xxxxxxxxxxxxx";ブラウザで見えるコードに書いた情報は、基本的にユーザーにも見えると考えます。
公開してよいキーと、秘密にすべきキーを混同しないようにしましょう。
フロントバリデーションだけでは不十分
フォームの章でも触れた通り、JavaScriptの入力チェックだけでは安全になりません。
ユーザーは、ブラウザのJavaScriptを無効化したり、開発者ツールで値を書き換えたりできます。
reCAPTCHAやhoneypot
問い合わせフォームでは、スパム対策としてreCAPTCHAやhoneypotを使うことがあります。
honeypotは、通常のユーザーには見えない入力欄を用意し、botがそこへ入力した場合にスパムと判定する考え方です。
ただし、スパム対策はサイトの要件やフォームシステムによって変わります。
この教材では詳しく扱いませんが、フロントだけで完全に防げるものではないと理解しておきましょう。
よくある失敗
品質面でよくある失敗を整理します。
- ライブラリや外部タグを増やしすぎて重くなる
- スクロールイベントに重い処理を入れてカクつく
divをボタン代わりにしてキーボード操作できない- 見た目だけ切り替えて、
aria-expandedなどの状態を更新していない - モーダルを閉じた後のフォーカス位置を考えていない
- ユーザー入力を
innerHTMLに入れてしまう - 秘密にすべきAPIキーをフロントに書いてしまう
- フロント側の入力チェックだけで安全だと思ってしまう
品質チェックリスト
納品前に、次の観点で確認しましょう。
- Consoleエラーが出ていないか
- 対象要素がないページでもエラーにならないか
- スマホでスクロールやタップが重くないか
- キーボードだけで操作できるか
- フォーカス位置が見えるか
- 開閉状態やエラー状態が属性にも反映されているか
- ユーザー入力を
innerHTMLに入れていないか - 秘密情報をフロントに書いていないか
- Lighthouseなどで大きな問題が出ていないか
この章のまとめ
この章では、JavaScriptの品質に関わる基本を学びました。
- JavaScriptは、表示速度や操作感に影響する
- ライブラリは便利だが、必要性を判断して使う
- スクロールイベントやDOM操作は重くしすぎない
- アニメーションは
transformとopacity中心に考える - UIはマウスだけでなくキーボードでも操作できるようにする
- 開閉状態やエラー状態は、ARIA属性や
hiddenにも反映する - ユーザー入力を
innerHTMLに入れない - フロント側だけで安全性を完結させない
次の章では、JavaScriptでエラーが出た時に、自分で原因を探すためのデバッグ方法を学びます。