なぜYouTubeでは動画を全画面表示しながらスクロールが可能なのか?
こちらは JavaScript Advent Calendar 2024の 25 日目の記事です。
はじめに
YouTubeは全画面表示中でもスクロールできて便利ですね[1]。
動画を全画面表示中でもスクロールできる
しかし Fullscreen API を用いて動画を全画面にした場合、通常はスクロール不可能なはずです。
YouTube ではどのようにスクロール可能な全画面を実現しているのでしょうか?
本記事では、その実装方法を解説します(Fullscreen APIとは)。
調査
そもそも Fullscreen API を使っているのか
まず YouTube で実際に Fullscreen API を使っているのか調べてみます(擬似的に実現している可能性も無くはないため)。
そのためには、動画を全画面化した後、コンソールにdocument.fullscreenElement
と入力します。
このプロパティは、全画面表示されている要素があればその要素を、なければnull
を返します。
document.fullscreenElementの結果
要素が返ってきたので、実際に Fullscreen API を使っているようでした。
ただ、返ってきた要素は<video>
ではなく<html>
になっています🤔
<html>
を全画面にしているのか
なぜ調べてみたところ、Fullscreen API の挙動は、対象がルート要素かどうかで大きく異なるようでした。
- 一般の要素の場合:全画面化すると要素の
position
がfixed
になり、スクロールできなくなる - ルート要素の場合:全画面化しても要素の
position
は変わらず、スクロールにも影響はない
具体的には、以下のようなCSSがユーザーエージェントにより付与されます(参考:WHATWGの仕様)。
/* 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]。
/* 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 のこの仕様は好きなので、採用してくれる動画サイトが増えると嬉しいですね。
最後までお読みいただきありがとうございました😊
-
ただしSafariやモバイル版YouTubeは除く ↩︎
-
:fullscreen擬似クラスのブラウザ対応状況はこちら ↩︎
Discussion