💻
DOM操作を減らしてWebページを高速化する方法
はじめに
JavaScriptでデータを表示する際、ループの中で何度もappendChild()を呼んでいませんか?実は、この書き方がページの表示を遅くしている原因かもしれません。
この記事では、DOM操作を最適化してページの読み込み速度を劇的に改善する方法を、初心者の方にもわかりやすく解説します。
対象読者
- JavaScriptの基本的な構文を理解している方
- APIからデータを取得して表示する処理を書いたことがある方
- ページの表示速度を改善したい方
問題のあるコード(Before)
まず、以前私が書いていた遅いコードを見てみましょう。ポケモンのデータをAPIから取得して、画面に151件表示するプログラムです。
処理の流れ
- APIで読み込んだデータを変数に入れる
-
document.createElement()でHTML要素を生成する - 生成した要素を
appendChild()でDOMに追加する
この3つの処理をループの中で繰り返していました。
for (i = 1; i < 151; i++) {
// 1. データを変数に入れる
let pokeName = data['name']
// 2. HTMLを生成する
let div = document.createElement('div');
let p = document.createElement('p');
p.innerHTML = pokeName;
div.appendChild(p);
// 3. 生成した要素を、#wrapperに追加する
document.getElementById('wrapper').appendChild(div);
}
何が問題なのか?
このコードの最大の問題は、DOM操作の回数が多すぎることです。
appendChild()を呼ぶたびに、ブラウザは以下の処理を行います:
- レイアウトの再計算(リフロー)
- 画面の再描画(リペイント)
つまり、151件のデータを表示する場合、151回も画面を描き直していることになります。これが表示速度を遅くしている原因です。
改良したコード(After)
DOM操作の回数を減らすために、HTMLを文字列として一度に作成し、最後に1回だけDOMに追加するように変更しました。
処理の流れ
- データをまとめて配列に格納
- HTML文字列を作成
- 最後に1回だけDOM操作で表示
// ========================================
// 1. データの作成
// ========================================
let pokemonArray = [];
let pokemon = {
id: "",
name: "",
};
for (let i = 0; i < 151; i++) {
// データをオブジェクトに追加
pokemon.id = data['id'];
pokemon.name = data['name'];
// 1モンスター毎に配列に追加
pokemonArray[i] = pokemon;
// オブジェクトを初期化
pokemon = {};
}
// ========================================
// 2. HTMLタグの作成(文字列として)
// ========================================
let html = "";
for (let i = 0; i < pokemonArray.length; i++) {
html +=
"<div>" +
"<span>" + pokemonArray[i].id + "</span>" +
"<span>" + pokemonArray[i].name + "</span>" +
"</div>";
}
// ========================================
// 3. 表示(DOM操作は1回だけ!)
// ========================================
const div = document.querySelector('#app');
div.innerHTML = html;
改善のポイント
① データとDOM操作を分離
// Before: ループ内でDOM操作
for (i = 1; i < 151; i++) {
document.getElementById('wrapper').appendChild(div); // 151回DOM操作
}
// After: データをまとめてから1回だけDOM操作
div.innerHTML = html; // 1回だけ!
② HTML文字列の結合
createElement()の代わりに、文字列としてHTMLを組み立てます:
// Before
let div = document.createElement('div');
let p = document.createElement('p');
p.innerHTML = pokeName;
div.appendChild(p);
// After
html += "<div><p>" + pokeName + "</p></div>";
パフォーマンスの比較
実際にどれくらい速くなるのか、簡易的な計測をしてみました:
| 方式 | 150件の表示時間 |
|---|---|
| Before(ループ内でDOM操作) | 約250ms |
| After(まとめて1回のDOM操作) | 約50ms |
約5倍の高速化を実現できました!
さらなる改善案
より高度な最適化として、以下の方法もあります:
DocumentFragmentを使う方法
innerHTMLを使いたくない場合は、DocumentFragmentを使うと安全です:
const fragment = document.createDocumentFragment();
for (let i = 0; i < pokemonArray.length; i++) {
const div = document.createElement('div');
div.textContent = pokemonArray[i].name;
fragment.appendChild(div); // メモリ上で組み立て
}
// 最後に1回だけDOMに追加
document.getElementById('app').appendChild(fragment);
テンプレートリテラルを使う方法
ES6のテンプレートリテラルを使うと、より読みやすくなります:
let html = "";
for (let i = 0; i < pokemonArray.length; i++) {
html += `
<div>
<span>${pokemonArray[i].id}</span>
<span>${pokemonArray[i].name}</span>
</div>
`;
}
まとめ
- DOM操作は重い処理なので、回数を減らすことが重要
- ループ内でのDOM操作は避け、まとめて1回で処理する
- データが多い場合は、HTML文字列を組み立ててから
innerHTMLで一括表示 - さらに安全性を求める場合は
DocumentFragmentを使う
この最適化により、ユーザーにストレスを与えない快適なWebページを作ることができます。ぜひ試してみてください!
Discussion