ブログ向けWordPressテーマ「SWELL」をリリースしました!

WordPressの投稿内の画像に自動で遅延読み込み機能(Lazyload)を適用させる方法【プラグインなし】

WordPressの記事で使用している画像にを自動で遅延読み込み(lazyload)させる方法をメモ。

lazyloadはプラグインでも実装できますが、'the_content'フックで画像タグを少しいじってあげるだけで、割と簡単に自力で適用させることができます。

また、今回Lazyloadで採用するスクリプトプラグインは、「lazysizes」として説明していきます。

必要なファイルはあらかじめ読み込んでおいてください。

参考:lazysozesの使い方

このlazysizeを使う場合、必要な作業は以下の3点です。

  1. imgタグにlazyloadというクラス名を付与する
  2. src属性をdata-src属性へ書き換える
  3. src属性にはプレースホルダー画像を設定する

では、順に処理を加えていきましょう!

目次

手順1:「lazyload」というクラス名を付与する

まずはlazyloadというクラス名をimgタグへ付与していきます。

記事の内容を出力するthe_content();に対するフィルターフックを追加し、正規表現を使ってimgタグのclassだけを書き換えます。

以下、functions.phpで読み込まれるようにコードは記述してください。

【簡易版】単純に、既存のclassにlazyloadを追加するコード

add_filter('the_content', function( $content ) {
  $content = preg_replace('/(<img[^>]*)\s+class="([^"]*)"/', '$1 class="$2 lazyload"', $content);
  return $content;
});

WordPressで画像を挿入する場合は基本的に何かしらのclassが付与されているので、上記のコードで特に問題はないとは思います。

ただ、もう少し厳密に処理しようとすると、

  • すでにclassを持つかどうか
  • その中にlazyloadというクラス名がすでについているかどうか

を考慮して処理を分岐させる必要がありますね。

それらを考えてコードを組み直すと、以下のようになります。

【しっかり版】classがない時などもちゃんと処理できるコード

add_filter('the_content', function( $content ) {
  $content = preg_replace_callback('/<img([^>]*)>/', function( $matches ) {
    $match = rtrim ( $matches[1], '/' );
    //classを持っているかどうか
    if ( strpos( $match, 'class=' ) !== false ) {

      //まだ'lazyload'クラスを持っていなければ追加
      if ( strpos( $match, 'lazyload' ) === false ) {
        $match = preg_replace('/class="([^"]*)"/', 'class="$1 lazyload"', $match);
      }

    } else {
      //classがなければ、classごと追加
      $match .= 'class="lazyload" ';
    }

    return '<img'. $match .'>';
  }, $content);
});

上記コードでは、imgタグの中身( <img~>)をpreg_replace_callback()で取得してから、その中でさらにclassなどの書き換えを行なっています。

おまけ情報:'the_conten'ではなく、'get_image_tag'というフィルターフックを使うと、クラシックエディターで画像を挿入する時点でclassを追加したりすることもできます。

手順2:src属性をdata-src属性に書き換える

lazyloadのクラス付与を簡易版で済ませている場合は、以下のようにするとimgタグのsrc属性値をそのままdata-srcへ変換できます。

【簡易版】src属性をdata-src属性に書き換える

add_filter('the_content', function( $content ) {

  /* 手順1の簡易版コード */

  $content = preg_replace('/(<img[^>]*)\s+src=/', '$1 data-src=', $content);
  return $content;
});

この時、プレースホルダー画像を設定するのであれば、以下のようになります。

【簡易版】src属性をdata-src属性に書き換える(プレースホルダー画像設置)

add_filter('the_content', function( $content ) {

  /* 手順1の簡易版コード */

  $placeholder = 'https://~/placeholder.gif';  //プレースホルダー画像のURLを変数に入れておく
  $content = preg_replace('/(<img[^>]*)\s+src=/', '$1 src="'. $placeholder .'" data-src=', $content);
  return $content;
});

さて、この手順2のコードですが、手順1で【しっかり版】コードを利用している場合はpreg_replace_callback()の処理中に入れてしまうことができます。

【しっかり版】コードの中でsrc属性をdata-src属性に書き換える(プレースホルダー画像設置)

add_filter('the_content', function( $content ) {
  $content = preg_replace_callback('/<img([^>]*)>/', function( $matches ) {
    $match = rtrim ( $matches[1], '/' );
    
    /* 手順1のしっかり版コード (略) */

    $placeholder = 'https://~/placeholder.gif';  //プレースホルダー画像のURLを変数に入れておく
    $match = str_replace(' src=', ' src="'. $placeholder .'" data-src=', $match);  //src属性をdata-srcへ

    return '<img'. $match .'>';
  }, $content);
});

この時の注意点

コンテンツの中で別のコンテンツを呼び出せるよう機能がある場合、'the_content'フックが2回走ってしまいます。

なので、そのような少し特殊なケースを考慮する場合、「src属性をdata-src属性に書き換える」処理が2重で走らないように、処理を分岐させなくてはいけません。

いくつか対処法はあると思いますが、「src属性に設定された画像がすでにプレースホルダー画像に置き換わっているかどうか」で条件分岐させるという方法があります。

srcをdata-srcに変える処理を2重処理されないようにするコード

$match = preg_replace_callback('/\ssrc="([^"]*)"/', function( $m ) {
  $imgsrc = $m[1];

  $placeholder = 'https://~/placeholder.gif';  //プレースホルダー画像のURLを変数に入れておく

  // まだ処理が加えられていない時だけ、srcをdata-srcに変える処理を実行
  if ( $imgsrc !== $placeholder ) {                
    return ' src="'. $placeholder .'" data-src="'. $imgsrc .'"';
  }
  return $m[0];

}, $match);

ほとんどの場合、ここまでする必要はないとは思いますが...!

今回の正規表現のポイント

今回のTipsで難しいポイントは正規表現部分だと思います。私もすぐに忘れてしまうので、ポイントとなる正規表現だけ、意味をまとめておきます。

記事をリライトしたため、今のコードでは使っていない正規表現も含まれてしまっています。

  • \s:空白文字
  • .:"n" を除く任意の 1 文字に一致
  • *:0回以上の繰り返し
  • +:1回以上の繰り返し
  • []:角括弧内の任意の1文字にマッチする
  • [0-9]:0~9の半角数字
  • [^{任意の文字}]:{任意の文字}以外の文字
  • [^>]*>以外の文字が0回以上繰り返している部分
  • "([^"]*)""から"までの中身全部
  • ():括弧内のパターンを1キャプチャとみなし、マッチした部分が順番に$1,$2などに渡される
  • foo(?!hoge):直後に「hoge」という文字列がない場合の「foo」という文字列にマッチ(否定先読み)

数字の指定は\dでも良いが、[0-9]の方が効率が良いらしい?

この記事が気に入ったら
フォローしてね!

目次
目次