Lazy Loadとsrcsetのsecretな関係

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
ぐぬぬの元ネタになったアナちゃん

この記事を三行にまとめると

Lazy Loadが無効になる
Lazy Loadを有効にするには
ぐぬぬ……
ブログで画像をいっぱい使っているとページの読み込みに時間がかかってしまうことがあります。そこで遅延読み込みなどを行うことがあると思います。

Wordpressの場合、例えばLazy Loadのようなプラグインを使えば簡単に遅延読み込みが実現できます。

と・こ・ろ・が。

このLazy Loadが無効になる場合が最近出てきてしまいました。今日はその秘密に迫ろうと思います。



無効になる条件

最近のWorpress(バージョン4.4辺りから)は、imgタグを使って画像を表示する場合に、自動的にsrcset属性をつけてくれるようになりました。

srcsetというのは、画面サイズに合わせて自動的に表示する画像を切り替えてくれるような属性です。Wordpressの場合、管理画面でメディアを追加すると、オリジナルの画像と一緒に異なるサイズの画像をいくつか作成してくれます。この複数の画像のうち表示する箇所に応じて最適な画像を自動で判定してくれるのがsrcset属性です。

んーと、例えばこんなimgタグがあったとします。

<img width="710" src="https://norm-nois.com/blog/wp-content/uploads/2017/05/four.jpg" class="attachment-full size-full wp-post-image" alt="よん" srcset="https://norm-nois.com/blog/wp-content/uploads/2017/05/four.jpg 710w, https://norm-nois.com/blog/wp-content/uploads/2017/05/four-304x300.jpg 304w" sizes="(max-width: 710px) 100vw, 710px" />

//srcset属性だけ抜き取るとこんな感じ
srcset="https://norm-nois.com/blog/wp-content/uploads/2017/05/four.jpg 710w, https://norm-nois.com/blog/wp-content/uploads/2017/05/four-304x300.jpg 304w"

詳しい説明は今回はあまり重要じゃないので省略しますが、srcset属性のところを見ると二つの画像のURLが書いてありますね。片方はオリジナルサイズ(横幅が710px)の画像、そしてもう一つは横幅が304pxの画像です。こんな風にsrcset属性を設定しておくと、その時の画面の状態に応じて最適な方を表示してくれるのがsrcset属性というわけです。横幅が300pxしかない時に710pxの大きな画像を表示する必要はありませんので、そういう時は304pxの方の画像が表示されると、そんな具合です。レスポンシブな画面で役に立つ機能ですね。

Wordpressで記事を作るとこのsrcsetを自動でセットしてくれるのであまり難しいことは考えなくて良いのですが、これがあるとLazy Loadが上手く機能してくれません。

実際に有効にしてみるとimgタグの中身はこんな感じになる。

<img width="710" src="https://norm-nois.com/blog/wp-content/plugins/lazy-load/images/1x1.trans.gif" class="attachment-full size-full wp-post-image" alt="よん" srcset="https://norm-nois.com/blog/wp-content/uploads/2017/05/four.jpg 710w, https://norm-nois.com/blog/wp-content/uploads/2017/05/four-304x300.jpg 304w" sizes="(max-width: 710px) 100vw, 710px" />

Lazy Loadはsrc属性をオリジナルの画像から1x1pxの軽い画像に置き換えて画面がスクロールされたらsrc属性を元の画像に戻すみたいな動きをします。上記のコードでもsrc属性の中身はgif画像になっていますね。でもsrcset属性の方は元画像のままです。imgタグの中にsrc属性とsrcset属性の両方がある場合はsrcset属性の方が優先されるので、srcの方がgif画像に置き換わっても結局元の画像を表示してしまうんですね。だから実は遅延読み込みが発生していない。



Lazy Loadを有効にするには

大きく分けてやり方は二つあります。

一つはプラグインの中身を改造してsrcset属性の方も画像が置き換えられるようにするというもの。使っているプラグインによっても違いますが、上記のLazy Loadの場合は、lazy-load.phpの中にある「process-image」というfunctionとlazy-load.jsの「lazy_load_image」というfunctionをちょこっと書き換えれば実現できます。

//lazy-load.php
static function process_image( $matches ) {
  // In case you want to change the placeholder image
  $placeholder_image = apply_filters( 'lazyload_images_placeholder_image', self::get_url( 'images/1x1.trans.gif' ) );

  $old_attributes_str = $matches[2];
  $old_attributes = wp_kses_hair( $old_attributes_str, wp_allowed_protocols() );

  if ( empty( $old_attributes['src'] ) ) {
    return $matches[0];
  }

  $image_src = $old_attributes['src']['value'];

  //serset属性があればsrcと同じように変数に元の画像を入れる
  $image_srcset = $old_attributes['srcset']['value'];

  // Remove src and lazy-src since we manually add them
  $new_attributes = $old_attributes;
  unset( $new_attributes['src'], $new_attributes['data-lazy-src'], $new_attributes['sreset'], $new_attributes['data-lazy-srcset'] );

  $new_attributes_str = self::build_attributes_string( $new_attributes );

  //srcと同じようにsrcset属性も1x1.trans.gifと置き換え
  return sprintf( '<img src="%1$s" data-lazy-src="%2$s" srcset="%3$s" data-lazy-srcset="%4$s" %5$s><noscript>%6$s</noscript>', esc_url( $placeholder_image ), esc_url( $image_src ), esc_url( $placeholder_image ), $image_srcset, $new_attributes_str, $matches[0] );
}

//lazy-load.js
function lazy_load_image( img ) {
  var $img = jQuery( img ),
    src = $img.attr( 'data-lazy-src' );

    //srcset属性にセットする値を取得
    srcset = $img.attr( 'data-lazy-srcset' );

  if ( ! src || 'undefined' === typeof( src ) )
    return;

  $img.unbind( 'scrollin' ) // remove event binding
    .hide()
    .removeAttr( 'data-lazy-src' )
    .removeAttr( 'data-lazy-srcset' )
    .attr( 'data-lazy-loaded', 'true' );

  img.src = src;

  //srcset属性の中身に元の値をセット
  img.srcset = srcset;
  $img.fadeIn();
}

分かりづらかったらすみません。ようはsrc属性にやっていることと全く同じことをsrcset属性にもやっちまおうというわけです。src属性はいったん「data-lazy-src」という属性に元の画像の値を移して「1×1.trans.gif」という画像に置き換え、javascriptでスクロールされた時にdata-lazy-srcの値を再びsrcに戻すという作業を行なってます。なので同じようにsrcset属性の中身を「data-lazy-srcset」という属性に移してgif画像と置き換え、javascriptでdata-lazy-srcsetの値をsrcset属性に戻すという動きを実装しています。



もう一つのやり方は、srcset属性の設定そのものを無効にしてしまうというやり方。Wordpressが自動でセットするのを止めてしまうというやり方もあります。こっちはだいぶ簡単。なんせfunctions.phpに以下の一行を加えればオーケーなので。

add_filter('wp_calculate_image_srcset', '__return_false');

これでsrcset属性が自動で入らなくなります。






どっちのやり方が良いかは何とも言えないのですが、プラグインをいずれ更新する可能性を考えたら、思いきってsrcset属性を無効にしてしまうのもありなのかもしれませんね。あるいはLazy Loadを止めてしまうというのも一つの手かもしれませんが。

あかつきのお宿も今は遅延読み込みをしてません。Lazy Loadを使うと他の動きに影響が出てしまって、それを解消する良い方法が思いつかないんで、とりあえずは画像を軽量化するくらいの対応しかできてないです。まあそれでも画像の最適化をしろって警告がGoogleのPageSpeed Insightsには言われてますけど。

ところでsrcsetってアナグラムでsecretになるじゃんって思ったけど、やってみたらならなかったわ。ぐぬぬ……。
 もしかしたら何か関連しているかも? 
 みんなからのコメント 
2017年06月06日 12:33:36
づや
srcsetがIE対応してないから切る派
2017年06月07日 10:27:01
まっち~(管理人)
>づやさん
そうなんですよね。やっぱりIEだよなぁ……。