💻

DOM操作を減らしてWebページを高速化する方法

に公開

はじめに

JavaScriptでデータを表示する際、ループの中で何度もappendChild()を呼んでいませんか?実は、この書き方がページの表示を遅くしている原因かもしれません。

この記事では、DOM操作を最適化してページの読み込み速度を劇的に改善する方法を、初心者の方にもわかりやすく解説します。

対象読者

  • JavaScriptの基本的な構文を理解している方
  • APIからデータを取得して表示する処理を書いたことがある方
  • ページの表示速度を改善したい方

問題のあるコード(Before)

まず、以前私が書いていた遅いコードを見てみましょう。ポケモンのデータをAPIから取得して、画面に151件表示するプログラムです。

処理の流れ

  1. APIで読み込んだデータを変数に入れる
  2. document.createElement()でHTML要素を生成する
  3. 生成した要素を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()を呼ぶたびに、ブラウザは以下の処理を行います:

  1. レイアウトの再計算(リフロー)
  2. 画面の再描画(リペイント)

つまり、151件のデータを表示する場合、151回も画面を描き直していることになります。これが表示速度を遅くしている原因です。

改良したコード(After)

DOM操作の回数を減らすために、HTMLを文字列として一度に作成し、最後に1回だけDOMに追加するように変更しました。

処理の流れ

  1. データをまとめて配列に格納
  2. HTML文字列を作成
  3. 最後に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