🎥

なぜYouTubeでは動画を全画面表示しながらスクロールが可能なのか?

2025/01/07に公開

こちらは JavaScript Advent Calendar 2024の 25 日目の記事です。
https://qiita.com/advent-calendar/2024/javascript

はじめに

YouTubeは全画面表示中でもスクロールできて便利ですね[1]


動画を全画面表示中でもスクロールできる

しかし Fullscreen API を用いて動画を全画面にした場合、通常はスクロール不可能なはずです。
YouTube ではどのようにスクロール可能な全画面を実現しているのでしょうか?

本記事では、その実装方法を解説します(Fullscreen APIとは)。

調査

そもそも Fullscreen API を使っているのか

まず YouTube で実際に Fullscreen API を使っているのか調べてみます(擬似的に実現している可能性も無くはないため)。

そのためには、動画を全画面化した後、コンソールにdocument.fullscreenElementと入力します。
このプロパティは、全画面表示されている要素があればその要素を、なければnullを返します。


document.fullscreenElementの結果

要素が返ってきたので、実際に Fullscreen API を使っているようでした。

ただ、返ってきた要素は<video>ではなく<html>になっています🤔

なぜ<html>を全画面にしているのか

調べてみたところ、Fullscreen API の挙動は、対象がルート要素かどうかで大きく異なるようでした。

  • 一般の要素の場合:全画面化すると要素のpositionfixedになり、スクロールできなくなる
  • ルート要素の場合:全画面化しても要素のpositionは変わらず、スクロールにも影響はない

具体的には、以下のようなCSSがユーザーエージェントにより付与されます(参考:WHATWGの仕様)。

実際に付与されるCSS(Chrome)
/* position:fixedにし、幅や高さを100%にする */
:not(:root):fullscreen {
    object-fit: contain;
    user-select: text;
    position: fixed !important;
    box-sizing: border-box !important;
    min-width: 0px !important;
    max-width: none !important;
    min-height: 0px !important;
    max-height: none !important;
    width: 100% !important;
    height: 100% !important;
    transform: none !important;
    inset: 0px !important;
    margin: 0px !important;
}
/* 背景を真っ黒にする */
:not(:root):fullscreen::backdrop {
    position: fixed;
    inset: 0px;
    background: black;
}
:fullscreen {
    overlay: auto !important;
}

/* スクロールバーを隠す */
:root:-webkit-full-screen-ancestor {
    overflow: hidden !important;
}

上記のCSSは、全画面化されているのがルート要素の場合には(overlayを除き)何もスタイルが当たらない形になっています。特にスクロールできなくなるようなスタイルは付与されません。

そのためYouTubeでは <html>を全画面化の対象とすることでスクロールロックを回避している ということのようでした。

実際「TVer」「AbemaTV」「ニコニコ動画」などスクロール不可能なサイトを見てみると、<html>ではなく<div><body>を全画面化しています。


ニコニコ動画の場合

また、<video>要素ネイティブの全画面化ボタンを押下した場合、当然 <html>ではなく<video>要素が全画面化してしまうはずですが、YouTube ではネイティブボタンは表示させず、独自のボタンを配置していました。

動画が全画面表示されているように見せる方法

以上でスクロール可能な理由は分かりましたが、これだけだと動画が全画面になっていないですね。
動画を「全画面表示風」にスタイリングする必要もあります。

この目的には:fullscreen擬似クラスが最適です[2]

動画が全画面表示されているように見せかけるCSS例
/* video要素のwrapper */
.video-container {
  display: grid;
  place-items: center;
  height: auto;
  width: 75%;
}
/* html要素が全画面のときに、wrapperの高さを100vhにする */
:root:fullscreen .video-container {
  height: 100vh;
  width: auto;
}
video {
  width: 100%;
}

ルート要素が全画面状態のとき<video>要素のWrapperの高さを100vhにし、<video>が全画面になっているように見せかけています(実は YouTube の方法とは少し異なるのですが本質的にはほぼ同じです。YouTube ではfullscreenという独自の属性にスタイルを当てています)。

まとめ

なぜYouTubeでは全画面表示していてもスクロールができるのか?

  • Fullscreen APIでは、ルート要素のみ全画面表示でもスクロールがロックされないようになっている
  • YouTubeでは、<video>要素ではなく<html>要素を全画面化することでスクロールロックを回避している
  • <html>要素を全画面化した際にCSSで<video>周りの height を調整することで、<video>が全画面化されたように見せかけている

実装例

ソースコードはこちらにあります。
またこちらのデモサイトで実際に挙動を試してみることができます(PCのみ対応)。

最後に

あけましておめでとうございます🌅
個人的に YouTube のこの仕様は好きなので、採用してくれる動画サイトが増えると嬉しいですね。
最後までお読みいただきありがとうございました😊

脚注
  1. ただしSafariやモバイル版YouTubeは除く ↩︎

  2. :fullscreen擬似クラスのブラウザ対応状況はこちら ↩︎

Discussion