いろいろな oEmbed 埋め込みコンテンツを遅延ロードしてみる

Lazy load という jQuery のプラグインで画像を遅延ロードさせてファーストビューの処理を高速化することが出来ます

私も luis-almeida/unveil · GitHub という軽量な遅延ロードをカスタマイズしたものを WordPress Plugin : Celtispack プラグインに組み込んで利用しています

先日、Celtispack プラグインのユーザーから Youtube 等の iframe で埋め込んだコンテンツを遅延ロード出来ないかという問い合わせを受け、せっかくなので他の埋め込みコンテンツに対しても遅延ロード出来るように機能追加してみました (^^)

Lazy Load

画像の遅延ロード

Lazy Load の基本的な機能が画像の遅延ロードです

仕組みや画像の遅延ロードに関しては下記を参照して下さい

画像遅延ロード Lazy Load (Unveil.js) で体感表示スピードアップ!
WordPress で画像が多いページの表示スピードを早くするための画像遅延ロード Lazy Load (Unveil.js)の設置方法を紹介します

WordPress 4.4 からサポートされたレスポンシブイメージ srcset も遅延ロード出来ます

画像遅延ロード Lazy Load をレスポンシブイメージ srcset に対応!
画像遅延ロード Lazy Load を WordPress4.4 から導入されたレスポンシブイメージ srcset に対応するためのプログラムサンプルを紹介します (^^)

埋め込みコンテンツの遅延ロード

iframe_lazy_load

WordPressでサポートしている oEmbed 埋め込みコンテンツは沢山の種類がありますが、私が使ったことのある埋め込みコンテンツを中心に対応してみました

実際のところ記事中に埋め込んでいるコンテンツが1つか2つならわざわざ遅延ロードするメリットはあまりないかも知れませんが、多数のコンテンツを埋め込んでいる場合は結構効果があるかも知れません (^^)

大きく分けて3パターンあります

埋め込まれているタグ 対象コンテンツ
iframe インラインフレーム youtube, vimeo, vine, slideshare, speakerdeck, codepen, soundcloud, wordpress-embed-content-card
blockquote 引用+スクリプト Twitter (tweet), instagram
a リンク+スクリプト Twitter (timeline / grid / moment)

※他に Facebook 等の利用者が多いものもありますが、今回は主に私が利用したことのある上記コンテンツのみに対応しています m(_ _)m

 

iframe 要素の遅延ロード

多くの動画やスライド系のサービスのコンテンツを埋め込む場合に iframe が使われています

例えば youtube なら次のような iframe が埋め込まれています

<iframe class='youtube-player' type='text/html' width='640' height='390' src="http://www.youtube.com/embed/kmfeKUNDDYs?version=3&#038;rel=1&#038;fs=1&#038;autohide=2&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;wmode=transparent"  allowfullscreen='true' style='border:0;'></iframe>

iframe には、src属性でインラインフレームとして表示するコンテンツのURLがセットされています

従って、このような iframe を遅延ロードするには、指定されている src属性を書き換え(ここでは data-src に変更)れば、ブラウザからは srcのURLが指定されていない事になりそのコンテンツの読み込みを一時的に停止することが出来ます

後は、lazy load の jQuery スクリプトを使い data-src を src に書き戻すことで遅延ロードすることができます

blockquote 要素の遅延ロード

ツイッターのツイートやインスタグラムが blockquote 引用文形式を使い埋め込まれています。また、引用文と合わせてスクリプトも埋め込まれているので、実際にはそのスクリプトがブラウザ上で動作することで最終的にインラインフレームとして読み込まれる2段階方式です

ツイートなら次のような blockquote が埋め込まれています

<blockquote src="twitter-tweet"  data-width="550"><p lang="en" dir="ltr">You can now shoot, edit and share video on Twitter. Capture life&#39;s most moving moments from your perspective. <a href="http://t.co/31JoMS50ha">pic.twitter.com/31JoMS50ha</a></p>
<p>&mdash; Twitter (@twitter) <a href="https://twitter.com/twitter/status/560070183650213889">January 27, 2015</a></p></blockquote>
<p><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p>

インスタグラムなら次の様な感じです

<blockquote src="instagram-media"  data-instgrm-captioned data-instgrm-version="5" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:640px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div>
</div>
<p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://instagram.com/p/NHb_XFAdot/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">Lookin&#39; good, @karlthefog.</a></p>
<p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A photo posted by CLR (@cherilucasrowlands) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2012-07-15T21:14:24+00:00">Jul 15, 2012 at 2:14pm PDT</time></p>
</div>
</blockquote>
<p><script async defer src="//platform.instagram.com/en_US/embeds.js"></script></p>

どちらの埋め込みも blockquote を使い src属性を対象埋め込みコンテンツであるか識別する為のキーワードとして利用しているようです。同時にロードしている各サービスのスクリプトがこのキーワードの blockquote を検出したら、それを該当する iframe インラインフレームコンテンツに置き換えているようです

従って、遅延ロードするには、指定されている src属性を書き換え(ここでは data-src に変更)れば、スクリプトは srcに指定されているキーワードが検出できないのでコンテンツの読み込みを一時的に停止することが出来ます

後は、lazy load の jQuery スクリプトを使い data-src を src に書き戻し、各スクリプトを再実行させれば遅延ロードすることができます

スクリプト再実行

ツイッター platform.twitter.com/widgets.js twttr.widgets.load()
インスタグラム platform.instagram.com/en_US/embeds.js window.instgrm.Embeds.process()

a リンク要素の遅延ロード

ツイッターのタイムライン(コレクション・グリッド)やモーメントでは a リンク形式を使い埋め込まれています。また、リンクと合わせてスクリプトも埋め込まれているので、実際にはそのスクリプトがブラウザ上で動作することで最終的にインラインフレームとして読み込まれる2段階方式です

コレクションタイムラインなら次のような a リンクが埋め込まれています

<p><a src="twitter-grid"  data-width="640" href="https://twitter.com/eno_celtislab/timelines/740068621104222210">水族館</a>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</p>

a リンクを使い src属性を対象埋め込みコンテンツであるか識別する為のキーワードとして利用しているようです。同時にロードしている各サービスのスクリプトがこのキーワードのリンクを検出したら、それを該当する iframe インラインフレームコンテンツに置き換えているようです

従って、遅延ロードするには、指定されている src属性を書き換え(ここでは data-src に変更)れば、スクリプトは srcに指定されているキーワードが検出できないのでコンテンツの読み込みを一時的に停止することが出来ます

後は、lazy load の jQuery スクリプトを使い data-src を src に書き戻し、ツイッターのスクリプト を twttr.widgets.load() で再実行させれば遅延ロードすることができます

コードサンプル

WordPress の Celtispack プラグイン のカスタム Lazy Load モジュールのコードを例に紹介します

PHP側の処理

PHPプログラム側の対応は、遅延ロードするタグごとに対象サービスか確認して src を data-src に書き換える機能を追加すればOKです

// unveil-lazyload.php から該当部分を抜粋しています

    static function unveil_blockquote($matches)
    {
        //$matches[0]  backquote タグ全体
        //$matches[1]  class 前の記述
        //$matches[2]  class 本体 (data-src 置き換えして一時的に無効化する)
        //$matches[3]  class 後の記述
        // data-src あれば unveil 記述済みなので除外
        $content = $matches[0];
        if (preg_match("#twitter|instagram#i", $matches[2])){
            if (!preg_match("#data-src#", $matches[0])){
                $content = '<blockquote' . $matches[1] . 'data-src="' . $matches[2] . '" '. $matches[3]. '>';
            }
        }
        return $content;
    }

    static function unveil_alink($matches)
    {
        //$matches[0]  a link タグ全体
        //$matches[1]  class 前の記述
        //$matches[2]  class 本体 (data-src 置き換えして一時的に無効化する)
        //$matches[3]  class 後の記述
        // data-src あれば unveil 記述済みなので除外
        $content = $matches[0];
        if (preg_match("#twitter-(moment|grid|timeline)#i", $matches[2])){
            if (!preg_match("#data-src#", $matches[0])){
                $content = '<a' . $matches[1] . 'data-src="' . $matches[2] . '" '. $matches[3]. '>';
            }
        }
        return $content;
    }

    public function unveil_lazyload($content)
    {
        $content = preg_replace_callback('#<img([^>]+?)src=[\'"]?([^\'"\s>]+)[\'"]?([^>]*)>#', "Celtispack_unveil_lazyload::unveil_attribute", $content);
        //style に background-image のみ定義している場合(celtis_s ホームカードを想定しているので他の style 要素も定義されているのは置き換えない) 
        $content = preg_replace_callback('#<([^>]+?)style=[\'"]background\-image:\s?(url\(.+?\));?[\'"]([^>]*)>#', "Celtispack_unveil_lazyload::unveil_bgimage", $content);
        //埋め込み要素の遅延ロード
        if(!empty($this->option['lazyload_embed'])){
            $content = preg_replace_callback('#<iframe([^>]+?)src=[\'"]?([^\'"\s>]+)[\'"]?([^>]*)>#', "Celtispack_unveil_lazyload::unveil_iframe", $content);
            $content = preg_replace_callback('#<blockquote([^>]+?)class=[\'"]?([^\'"\s>]+)[\'"]?([^>]*)>#', "Celtispack_unveil_lazyload::unveil_blockquote", $content);
            $content = preg_replace_callback('#<a([^>]+?)class=[\'"]?([^\'"\s>]+)[\'"]?([^>]*)>#', "Celtispack_unveil_lazyload::unveil_alink", $content);
        }
        return $content;
    }
JSスクリプト側の処理

Unveil.js スクリプト側では、src に data-src の値をセットして元の状態に戻し、不要となった data-src を削除して、必要に応じてスクリプトの再ロードを行います

jQuery(document).ready(function($){
    ;(function($) {

      $.fn.unveil = function(threshold, callback) {

        var $w = $(window),
            th = threshold || 0,
            images = this,
            loaded;

        this.one("unveil", function() {
          var source = this.getAttribute("data-src");
          var srcset = this.getAttribute("data-srcset");
          var sizes  = this.getAttribute("data-sizes");
          
          if (source) {
            if ($(this).is("img")) {
                $(this).attr("src", source);
                if (srcset){
                    $(this).attr("srcset", srcset);
                    $(this).removeAttr("data-srcset");
                }
                if (sizes){
                    $(this).attr("sizes", sizes);
                    $(this).removeAttr("data-sizes");
                }
            } else {
                $cval = $(this).css("background-image");
                if($cval == "none")
                    $(this).css("background-image", source);

                if(celtispack_options.lazyload_embed == "1"){
                    if ($(this).is("iframe")) {
                        $(this).attr("src", source);
                        $(this).removeAttr("data-src");
                        //fitVids 有効なら実行する
                        if ( 'undefined' != typeof $(".entry-content,.oembed-widget").fitVids ) {
                            $(".entry-content,.oembed-widget").fitVids({
                                customSelector: [
                                    "iframe[src*='vine']", 
                                    "iframe[src*='ustream']",
                                    "iframe[src*='slideshare']",
                                    "iframe[src*='speakerdeck']",
                                    "iframe[src*='w.soundcloud.com'][src*='visual=true']"
                            ]
                            });
                        }
                    } else if ($(this).is("blockquote")) {
                        $(this).addClass(source);
                        $(this).removeAttr("data-src");
                        if(source.match(/twitter/)){
                            if ('undefined' != typeof twttr)
                                twttr.widgets.load();
                        } else if(source.match(/instagram/)){
                            if ('undefined' != typeof window.instgrm)
                                window.instgrm.Embeds.process();
                        }
                    } else if ($(this).is("a")) {
                        $(this).addClass(source);
                        $(this).removeAttr("data-src");
                        if(source.match(/twitter/)){
                            if ('undefined' != typeof twttr)
                                twttr.widgets.load();
                        }
                    }
                }
            }
            if (typeof callback === "function") callback.call(this);
          }
        });

        function unveil() {
          var inview = images.filter(function() {
            var $e = $(this);
            if ($e.is(":hidden")) return;

            var wt = $w.scrollTop(),
                wb = wt + $w.height(),
                et = $e.offset().top,
                eb = et + $e.height();

            return eb >= wt - th && et <= wb + th;
          });

          loaded = inview.trigger("unveil");
          images = images.not(loaded);
        }

        $w.scroll(unveil);
        $w.resize(unveil);

        unveil();

        return this;

      };

    })(window.jQuery || window.Zepto);
    
    $("*[data-src]").unveil(400);
});

ちなみに、この機能は、公開中の Celtispack プラグインの Ver2.0.0 に実装済みです

まだ、不具合があるかも知れませんが、興味がある方は試してみてください (^^)

 


まとめ記事紹介

いろいろな oEmbed 埋め込みコンテンツを遅延ロードしてみる” への8件のコメント

  1. コードを参考にプラグイン化して確認したのですが、
    Twitter(ツイート埋め込み)・Instagram 両方の埋め込みが表示されていないようです。
    (画像付きのTwitter・Instagram で、画像が表示されていないという意味になります)
    URL:http://celtislab.net/archives/20141201/instagram-thumbnail/

    不具合?かなと思いますが、上記URLから確認取れましたらお手数ですが修正等お願い致します。

    1. 私の環境では画像が表示されないという不具合を確認できませんでした
      不具合の発生する埋め込みURLを教えてください

      1. 左様でございますか。

        表に自サイトを提示する事ができませんので、
        お問い合わせを通して自サイトを提示致しました。

        大変お手数おかけしますがご確認お願い致します。

        1. 私の環境では以下の2つともやはり問題なく表示されます
          Instggram url : https://www.instagram.com/p/cFLan6OBUs/
          Twitter url : https://twitter.com/MarquardtA/status/359581029004107776

          data-src に置き換えられていれば PHP 側の処理はうまくいっていますので、jQuery 側が正常に動作していないようです
          スクリプトで何らかのエラーがあり再実行 twttr.widgets.load() や window.instgrm.Embeds.process() が実行できていないのかもしれません

          1. お問い合わせにて「試した事」にて追記忘れたのですが、
            全プラグイン停止しても変わらなかったです。

            ご確認していただき、アドバイスをありがとうございます。
            いただいたアドバイスからjQueryのバージョンを2に下げて確認してみます。

  2. 確認結果の報告です。

    バージョンを下げる以外に私は、
    修正 or 対応の仕方が分からないのですが、
    jQueryのバージョンを2 or 1.12.4に下げて、
    確認したところ表示されず解決できなかったです。

    1. 提示されたサイトの unveil_custom.js を見てみたら元のコードから修正を加えているようでかっこ } の対応が崩れて余分に挿入されています。
      その為にスクリプトが正常に動作していないようです

      1. 再度ご確認していただきありがとうございます。

        正常に動作していないという事で、
        JS ファイルを確認する方法調べて、
        確認するツールで確認したらエラーがあり、
        四苦八苦しながらこうかなとコードを編集してエラーが表示されなかったので、
        今あるJS ファイルに上書き更新して確認したら正常に画像が表示されていました。

        data-src 付与された状態で画像が表示されているのを現在確認できると思います。

コメントを残す

search star user home refresh tag chevron-left chevron-right exclamation-triangle calendar comment folder thumb-tack navicon angle-double-up angle-double-down angle-up angle-down quote-left googleplus facebook instagram twitter rss