YouTube埋め込みページを高速化する方法【Lite YouTube Embedでiframeの遅延読み込み】

  • ホームページにYouTube動画を埋め込んでいて重い
  • YouTube動画を埋め込んだ記事の表示速度が遅いので改善したい
  • iframe内にloading="lazy"を追記しても変わらない
  • 高速化のためにプラグインを増やしたくない

上記のような状況の方はこの記事を参考にしてみてください。

高速化実施前のPageSpeed Insightsの計測結果

スコア33
高速化実施前

YouTube動画を複数埋め込んだページの表示速度を計測してみました。

高速化実施前にPageSpeed Insightsで表示速度を計測したところ、モバイルのスコアが33でした。

LCP(Largest Contentful Paint)、TTL(Time to Interactive)などのパフォーマンスが悪いですね。これらはGoogleが検索順位の決定指標の一つとしている数値(コアウェブバイタル)に影響する可能性があるので改善しましょう。

改善項目リスト
改善できる項目の一番上には「使用していないJavaScriptの削減」が出現!

スコアの下には改善できる項目が表示されています。

ダントツで「使用していないJavaScriptの削減」が出ていますね…

YouTube関係のJavaScriptが読み込まれていることがわかる

タブを開くと、

en_US/base.js (www.youtube.com)

www-embed-player.vflset/www-embed-player.js (www.youtube.com)

PageSpeed Insights

と表示されているので、YouTubeの埋め込み関係のJavaScriptが読み込まれていることが分かります。

動画の埋め込み(iframe)を遅延読み込みできないかと調べてみたのですが、Google Developersで紹介されているLite YouTube Embedが良さそうだったので導入方法をご紹介します。

※WordPress 5.7以降ではiframeタグにloading="lazy"が自動で付与されるので手動でわざわざ入れなくてもOKですが、ファーストビューでは遅延読み込みが適用されないので別の手法が必要というわけですね。

Lite YouTube Embedのライセンスは「Apache License 2.0」なので、改変や複製だけでなく商用利用も可能です。

Lite YouTube Embedの導入方法

作業の大まかな流れは以下の通りです。

  • CSSを読み込ませる
  • JavaScriptを読み込ませる
  • HTMLを編集する

それぞれ解説します。

1. CSSを読み込ませる

下記のCSSコードをhead内で読み込ませます。

lite-youtube {
    background-color: #000;
    position: relative;
    display: block;
    contain: content;
    background-position: center center;
    background-size: cover;
    cursor: pointer;
    max-width: 720px;
}

/* gradient */
lite-youtube::before {
    content: '';
    display: block;
    position: absolute;
    top: 0;
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
    background-position: top;
    background-repeat: repeat-x;
    height: 60px;
    padding-bottom: 50px;
    width: 100%;
    transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}

/* responsive iframe with a 16:9 aspect ratio
    thanks https://css-tricks.com/responsive-iframes/
*/
lite-youtube::after {
    content: "";
    display: block;
    padding-bottom: calc(100% / (16 / 9));
}
lite-youtube > iframe {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    border: 0;
}

/* play button */
lite-youtube > .lty-playbtn {
    display: block;
    width: 68px;
    height: 48px;
    position: absolute;
    cursor: pointer;
    transform: translate3d(-50%, -50%, 0);
    top: 50%;
    left: 50%;
    z-index: 1;
    background-color: transparent;
    /* YT's actual play button svg */
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/><path d="M45 24 27 14v20" fill="white"/></svg>');
    filter: grayscale(100%);
    transition: filter .1s cubic-bezier(0, 0, 0.2, 1);
    border: none;
}

lite-youtube:hover > .lty-playbtn,
lite-youtube .lty-playbtn:focus {
    filter: none;
}

/* Post-click styles */
lite-youtube.lyt-activated {
    cursor: unset;
}
lite-youtube.lyt-activated::before,
lite-youtube.lyt-activated > .lty-playbtn {
    opacity: 0;
    pointer-events: none;
}

.lyt-visually-hidden {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }

WordPressの場合、テーマやプラグインにそのページのみにCSSを適用する機能(カスタムCSSなど)があれば活用すると良いでしょう。(テーマの機能に無い場合はプラグイン「Code Snippets」での拡張がおすすめです。)

2. JavaScriptを読み込ませる

下記のJSコードをbody閉じタグ直前で読み込ませます。

/**
 * A lightweight youtube embed. Still should feel the same to the user, just MUCH faster to initialize and paint.
 *
 * Thx to these as the inspiration
 *   https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
 *   https://autoplay-youtube-player.glitch.me/
 *
 * Once built it, I also found these:
 *   https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube (👍👍)
 *   https://github.com/Daugilas/lazyYT
 *   https://github.com/vb/lazyframe
 */
class LiteYTEmbed extends HTMLElement {
    connectedCallback() {
        this.videoId = this.getAttribute('videoid');

        let playBtnEl = this.querySelector('.lty-playbtn');
        // A label for the button takes priority over a [playlabel] attribute on the custom-element
        this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play';

        /**
         * Lo, the youtube placeholder image!  (aka the thumbnail, poster image, etc)
         *
         * See https://github.com/paulirish/lite-youtube-embed/blob/master/youtube-thumbnail-urls.md
         *
         * TODO: Do the sddefault->hqdefault fallback
         *       - When doing this, apply referrerpolicy (https://github.com/ampproject/amphtml/pull/3940)
         * TODO: Consider using webp if supported, falling back to jpg
         */
        if (!this.style.backgroundImage) {
          this.style.backgroundImage = `url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`;
        }

        // Set up play button, and its visually hidden label
        if (!playBtnEl) {
            playBtnEl = document.createElement('button');
            playBtnEl.type = 'button';
            playBtnEl.classList.add('lty-playbtn');
            this.append(playBtnEl);
        }
        if (!playBtnEl.textContent) {
            const playBtnLabelEl = document.createElement('span');
            playBtnLabelEl.className = 'lyt-visually-hidden';
            playBtnLabelEl.textContent = this.playLabel;
            playBtnEl.append(playBtnLabelEl);
        }

        // On hover (or tap), warm up the TCP connections we're (likely) about to use.
        this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true});

        // Once the user clicks, add the real iframe and drop our play button
        // TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time
        //   We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003
        this.addEventListener('click', this.addIframe);
    }

    // // TODO: Support the the user changing the [videoid] attribute
    // attributeChangedCallback() {
    // }

    /**
     * Add a <link rel={preload | preconnect} ...> to the head
     */
    static addPrefetch(kind, url, as) {
        const linkEl = document.createElement('link');
        linkEl.rel = kind;
        linkEl.href = url;
        if (as) {
            linkEl.as = as;
        }
        document.head.append(linkEl);
    }

    /**
     * Begin pre-connecting to warm up the iframe load
     * Since the embed's network requests load within its iframe,
     *   preload/prefetch'ing them outside the iframe will only cause double-downloads.
     * So, the best we can do is warm up a few connections to origins that are in the critical path.
     *
     * Maybe `<link rel=preload as=document>` would work, but it's unsupported: http://crbug.com/593267
     * But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.
     */
    static warmConnections() {
        if (LiteYTEmbed.preconnected) return;

        // The iframe document and most of its subresources come right off youtube.com
        LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');
        // The botguard script is fetched off from google.com
        LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');

        // Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.
        LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
        LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');

        LiteYTEmbed.preconnected = true;
    }

    addIframe(e) {
        if (this.classList.contains('lyt-activated')) return;
        e.preventDefault();
        this.classList.add('lyt-activated');

        const params = new URLSearchParams(this.getAttribute('params') || []);
        params.append('autoplay', '1');

        const iframeEl = document.createElement('iframe');
        iframeEl.width = 560;
        iframeEl.height = 315;
        // No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include
        iframeEl.title = this.playLabel;
        iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
        iframeEl.allowFullscreen = true;
        // AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL
        // https://stackoverflow.com/q/64959723/89484
        iframeEl.src = `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`;
        this.append(iframeEl);

        // Set focus for a11y
        iframeEl.focus();
    }
}
// Register custom element
customElements.define('lite-youtube', LiteYTEmbed);

3. HTMLを編集

以下のようにHTMLを編集します。paramsにrel=0を設定することで関連動画を非表示にできます。

<lite-youtube videoid="ここにYouTube動画ID" params="rel=0"></lite-youtube>

videoidにはYouTube動画のIDを設定します。

共有>URLの末尾が動画のID

IDは動画共有のURL末尾でわかります。

高速化実施前後のPageSpeed Insights計測結果比較

スコア33
高速化実施前
高速化実施後

Time to Interactiveが15.1sから3.7sへと大幅に改善されました。LCPも1.8s短縮できました。

今回ご紹介した対処法で意外と簡単に解決できるので、「YouTube動画を埋め込んだページの読み込みが遅くて困っている!」という方は是非試してみてください。

懸念点

モバイルでは2回クリックが必要になるケースがあります。(1タップ後にiframeが設置されるが自動再生が効かないため2タップ目で再生できる)

自分のAndroid端末では2タップ必要でした。

質問集

YouTubeを埋め込むと表示速度が遅くなる原因は?

YouTubeの再生に必要なプレイヤーを読み込む際にJavaScriptが読み込まれ、ページの描画処理がブロックされるので表示が遅くなります。

どのような方法で改善しますか?

ライブラリ「Lite YouTube Embed」を使用することで初期表示にiframeを使用せず、クリックされた際にiframeで動画を表示・再生させます。初期表示には画像を使用します。

ページスピード改善などWebサイトの困りごとはSenriWebにお任せください

「WordPressサイトでページスピードが上がらなくて困っている」「WordPressサイトで問題が発生して正しく動作しない」などでお困りの場合は、是非SenriWebにお問い合わせください。お使いのサイトの状況に合わせて有効な対処法をご提案いたします。

>>SenriWebに相談する

参考サイト