2026年9月26日

2026年9月26日

WordPressの遅延読み込みエラーを解決する方法

はじめに

WordPressサイトで画像にloading="lazy"が付いているのにスクロールしても読み込まれない・サードパーティの遅延読み込みプラグインを入れたら特定のスライダーで画像が表示されなくなった・JavaScriptコンソールにIntersectionObserver is not definedというエラーが出ている・iOSのSafariで遅延読み込みが動作せずページが真っ白になるといった問題は、ブラウザのネイティブlazy loadとJavaScriptベースの遅延読み込みの競合・古いブラウザ対応漏れ・above-the-fold(ファーストビュー)への誤適用が原因です。

症状・原因

  • ファーストビュー(スクロールなしで見える範囲)の画像にもloading="lazy"が付いてLCPが悪化している
  • JavaScriptの遅延読み込みプラグインとWordPressネイティブのlazy loadが二重に動作して競合している
  • スライダープラグイン(Swiper、Slick)が初期化前に遅延読み込みで画像を隠してしまい表示されない
  • data-srcを使った遅延読み込みがCSSでdisplay:noneのタブ内の画像を読み込まない

解決手順

ステップ1:遅延読み込みの現状を診断する

# WP-CLIで遅延読み込み設定を確認
wp eval "
// ネイティブlazy loadの設定確認
echo 'wp_lazy_loading_enabled: ' . (apply_filters('wp_lazy_loading_enabled', true, 'img', 'the_content') ? 'true' : 'false') . PHP_EOL;

// アイキャッチ画像のloading属性確認
\$posts = get_posts(['posts_per_page' => 3]);
foreach (\$posts as \$post) {
    \$thumb_id = get_post_thumbnail_id(\$post->ID);
    if (\$thumb_id) {
        \$img = wp_get_attachment_image(\$thumb_id, 'medium');
        preg_match('/loading=[\"\'](.*?)[\"\']/', \$img, \$m);
        echo \$post->post_title . ': loading=' . (\$m[1] ?? 'none') . PHP_EOL;
    }
}
"

# アクティブな遅延読み込み関連プラグイン確認
wp plugin list --status=active --format=table | grep -i lazy

ステップ2:ネイティブlazy loadを正しく設定する

// functions.php: ネイティブlazy loadの最適化

// ① ファーストビューの画像にはeager(即時読み込み)を設定
add_filter('wp_get_attachment_image_attributes', function(array $attr, WP_Post $attachment, $size): array {
    // アイキャッチ画像は通常ファーストビューにあるのでeagerに
    if (is_singular() && $attachment->ID === get_post_thumbnail_id()) {
        $attr['loading']          = 'eager';
        $attr['fetchpriority']    = 'high';
    } else {
        $attr['loading']          = 'lazy';
        $attr['decoding']         = 'async';
    }
    return $attr;
}, 10, 3);

// ② コンテンツ内の最初の画像はeagerに
add_filter('the_content', function(string $content): string {
    static $first_image_found = false;

    if ($first_image_found) {
        return $content;
    }

    // 最初のimgタグのloading=lazyをeagerに変更
    $content = preg_replace_callback(
        '/<img([^>]*loading=["\']lazy["\'][^>]*)>/i',
        function(array $m) use (&$first_image_found): string {
            if ($first_image_found) {
                return $m[0];
            }
            $first_image_found = true;
            return '<img' . str_replace('loading="lazy"', 'loading="eager"', $m[1]) . '>';
        },
        $content
    );
    return $content;
});

// ③ 管理画面では無効化
add_filter('wp_lazy_loading_enabled', function(bool $default, string $tag_name, string $context): bool {
    if (is_admin()) {
        return false;
    }
    return $default;
}, 10, 3);

ステップ3:JavaScriptベースの遅延読み込みを実装する

// assets/js/lazy-load.js: IntersectionObserver を使った安全な遅延読み込み

(function() {
    'use strict';

    // IntersectionObserver非対応ブラウザへのフォールバック
    if (!('IntersectionObserver' in window)) {
        // 全画像を即座に読み込む
        document.querySelectorAll('img[data-src]').forEach(function(img) {
            img.src = img.dataset.src;
            if (img.dataset.srcset) {
                img.srcset = img.dataset.srcset;
            }
        });
        return;
    }

    var observer = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
            if (!entry.isIntersecting) return;

            var img = entry.target;

            // data-srcからsrcに切り替え
            if (img.dataset.src) {
                img.src = img.dataset.src;
                delete img.dataset.src;
            }
            if (img.dataset.srcset) {
                img.srcset = img.dataset.srcset;
                delete img.dataset.srcset;
            }

            img.classList.add('loaded');
            observer.unobserve(img);
        });
    }, {
        rootMargin: '200px 0px',  // 200px手前から読み込み開始
        threshold:  0
    });

    // 遅延読み込み対象の画像を監視
    document.querySelectorAll('img[data-src]').forEach(function(img) {
        observer.observe(img);
    });
})();
// functions.php: 遅延読み込みJSを登録
add_action('wp_enqueue_scripts', function(): void {
    wp_enqueue_script(
        'lazy-load',
        get_theme_file_uri('assets/js/lazy-load.js'),
        [],
        '1.0.0',
        true  // フッターに出力
    );
});

// コンテンツのimgをdata-src形式に変換(JSベースの場合)
add_filter('the_content', function(string $content): string {
    return preg_replace_callback(
        '/<img([^>]*)src=["\']([^"\']+)["\']([^>]*)>/i',
        function(array $m): string {
            // ファーストビュー画像はスキップ
            if (str_contains($m[1] . $m[3], 'fetchpriority="high"')) {
                return $m[0];
            }
            $attrs = $m[1] . ' data-src="' . $m[2] . '"' . $m[3];
            return '<img' . $attrs . '>';
        },
        $content
    );
});

ステップ4:スライダーとタブの競合を解決する

// functions.php: スライダー・タブ内の画像を除外

// ① スライダープラグインの画像は遅延読み込みから除外
add_filter('wp_lazy_loading_enabled', function(bool $enabled, string $tag): bool {
    // スライダーのコンテキストでは無効化
    if (doing_filter('the_content') && isset($GLOBALS['_wp_lazy_load_skip'])) {
        return false;
    }
    return $enabled;
}, 10, 2);

// ② タブ・アコーディオン内の画像はロード後に監視を再実行
add_action('wp_footer', function(): void {
    ?>
    <script>
    // タブ切り替え時に遅延読み込みを再トリガー
    document.addEventListener('tab-changed', function(e) {
        var panel = e.detail && e.detail.panel;
        if (!panel) return;
        panel.querySelectorAll('img[data-src]').forEach(function(img) {
            img.src = img.dataset.src;
            img.removeAttribute('data-src');
        });
    });

    // display:none から表示されたときのCSSアニメーション
    var style = document.createElement('style');
    style.textContent = 'img.loaded { animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }';
    document.head.appendChild(style);
    </script>
    <?php
});

ステップ5:背景画像とiframeの遅延読み込みを実装する

// functions.php: 背景画像とiframeの遅延読み込み

// ① iframe(YouTube埋め込み)を遅延読み込み
add_filter('the_content', function(string $content): string {
    return preg_replace_callback(
        '/<iframe([^>]*src=["\'][^"\']*youtube[^"\']*["\'][^>]*)>/i',
        function(array $m): string {
            // loading="lazy"がなければ追加
            if (!str_contains($m[1], 'loading=')) {
                return '<iframe' . $m[1] . ' loading="lazy">';
            }
            return $m[0];
        },
        $content
    );
});

// ② CSS背景画像を遅延読み込み(data-bg属性を使用)
add_action('wp_footer', function(): void {
    ?>
    <script>
    if ('IntersectionObserver' in window) {
        var bgObserver = new IntersectionObserver(function(entries) {
            entries.forEach(function(entry) {
                if (!entry.isIntersecting) return;
                var el = entry.target;
                if (el.dataset.bg) {
                    el.style.backgroundImage = 'url(' + el.dataset.bg + ')';
                    el.removeAttribute('data-bg');
                    bgObserver.unobserve(el);
                }
            });
        }, { rootMargin: '100px' });

        document.querySelectorAll('[data-bg]').forEach(function(el) {
            bgObserver.observe(el);
        });
    }
    </script>
    <?php
});
# Chrome DevToolsでlazy loadの動作確認
# Network タブ → Img フィルター → スクロール時に読み込まれることを確認

# Lighthouse でLCPのlazy load誤適用を検出
# "Largest Contentful Paint image was lazily loaded" 警告が出ていないか確認
wp eval "
// ファーストビューの画像リスト(参考用)
\$thumb_ids = [];
\$posts = get_posts(['posts_per_page' => 5]);
foreach (\$posts as \$p) {
    \$id = get_post_thumbnail_id(\$p->ID);
    if (\$id) echo wp_get_attachment_url(\$id) . PHP_EOL;
}
"

注意事項

  • ファーストビュー(above-the-fold)に表示される画像にloading="lazy"を設定するとLCPスコアが悪化します。Lighthouseの「Largest Contentful Paint element」で特定してeagerまたはfetchpriority="high"を設定してください
  • IntersectionObserverはIE11で未対応です。必要に応じてpolyfillを追加するか、未対応ブラウザでは全画像を即時読み込みするフォールバックを実装してください
  • WordPressのネイティブlazy loadとJavaScriptベースの遅延読み込みプラグインを同時に使うと二重処理になります。どちらかに統一することをお勧めします

まとめ

WordPress遅延読み込みエラーの解決は①wp_lazy_loading_enabledフィルターで設定確認・アクティブプラグインの競合チェック・ファーストビュー画像のloading属性確認、②アイキャッチ画像をloading=eager+fetchpriority=highに・コンテンツ内最初の画像をeagerに変更・管理画面では無効化、③IntersectionObserverでdata-src→srcの切り替えを実装・非対応ブラウザは全画像即時読み込み・rootMargin:200pxで早めに読み込み開始、④スライダー・タブ内の画像はlazy load除外・tab-changedイベントで表示時に読み込みを再実行・fadeInアニメーションで自然な表示、⑤YouTubeなどiframeにloading=lazy追加・CSS背景画像はdata-bg属性+IntersectionObserverで遅延読み込みの手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲