WordPressの記事で使用している画像にを自動で遅延読み込み(lazyload)させる方法をメモ。
lazyloadはプラグインでも実装できますが、'the_content'
フックで画像タグを少しいじってあげるだけで、割と簡単に自力で適用させることができます。
また、今回Lazyloadで採用するスクリプトプラグインは、「lazysizes」として説明していきます。
必要なファイルはあらかじめ読み込んでおいてください。
このlazysizeを使う場合、必要な作業は以下の3点です。
- imgタグに
lazyload
というクラス名を付与する - src属性をdata-src属性へ書き換える
- 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]
の方が効率が良いらしい?
コメント
コメント一覧 (2件)
とても参考になりました。ありがとうございます。
一点、Wordpress導入の場合、画像ファイル名が「~~-xaaa-300×240.jpg」のようなときに、js側luminusコードが正常動作しなくなることがわかりました。
手前の-xを置換してしまい、残したくないサイズ部分が残ってしまっていました。
そのため、
var fullSrc = imgSrc.replace(/-[0-9]*x[0-9]*/, “”);
を
var fullSrc = imgSrc.replace(/-[0-9]*x[0-9]*.jpg/, “.jpg”);
などに変更する必要があるようです。
以上、ご報告でした。
ありがとうございました。
本当ですね!ご指摘ありがとうございます!
改善しておきます!