JavaScriptで動画のホバー再生・停止を実装する
仕事で JavaScript を使った動画のホバー再生を実装した時に、色々苦戦したのでメモです。
やりたいこと
- 動画一覧ページで表示している video タグに対してホバーした時に動画を再生したい
- ホバーを外したときには動画再生を停止したい
- イメージとしてはshutterstokやAdobe stockのような感じ
video タグには preload: none を付与
動画の一覧で表示する video タグには、preload
属性を none に指定します。こうすることでブラウザが事前に読み込むことがなくなるため、パフォーマンスが悪化するのを防ぐことができます。
video - The Video Embed element - HTML: HyperText Markup Language | MDN
シンプルなホバー再生を実装してみる
シンプルに全ての video タグに対して、mouseover
とmouseout
のイベントリスナーを登録してみます。
この実装だと、DOMException: The play() request was interrupted
というエラーがコンソール上に表示されます。
document.querySelectorAll("video").forEach((video) => {
video.addEventListener("mouseover", () => {
video.play();
});
video.addEventListener("mouseout", () => {
video.pause();
});
});
Google の開発ブログにこのエラーに関しての記事があります。
DOMException: The play() request was interrupted | Web
何が起こっているのかを記事から意訳すると以下の通りです。
video.play()
が動画コンテンツを非同期で読み込み始めるvideo.pause()
が動画の読み込みに割り込みする(動画の読み込みがまだ完了していないので)video.play()
が非同期的に拒否する
上記のコード例だと、mouseover
した時に動画読み込みを開始してすぐにmouseout
されると、動画の読み込みが完了していないところで、pause()
が走ってしまうので、DOMException: The play() request was interrupted
エラーになるという感じです。
video.pause()
を改善する
動画を停止するところのコードを改善しました。play()
が Promise を返すので、video.play().then()
とすることでpause()
が動画再生に割り込みすることがなくなります。
document.querySelectorAll("video").forEach((video) => {
video.addEventListener("mouseover", () => {
video.play();
});
video.addEventListener("mouseout", () => {
// pauseが非同期のplayを邪魔しないよう、thenの中に入れる
video.play().then(function () {
video.pause();
});
});
});
補足: Firefox を使うとイベントリスナーの確認がしやすい
上記実装をするにあたり他の動画系サイトの実装を参考にしました。その際、Firefox の検証ツールが便利でした。 こんな感じで各 DOM にどういったイベントが紐付いているかを簡単に調べることができます。
Firefoxの検証機能便利だな👀
— Kenzo Tanaka (@kenzoooooB) April 29, 2021