トップページのメインビジュアルに大きく動画を配置して再生する手法は、良く使われるデザイン・演出になりました。
しかし、スマートフォンのようなデバイスでは、動画を再生するためにダウンロードに時間がかかり、パケット通信量も画像に比べて増えてしまうことから、スマートフォンでは動画を再生するのではなく、静止画を表示することもあります。
このとき、どのデバイスでも静止画を表示することになれば簡単なのですが、パソコンは動画を再生して、スマートフォンでは画像を表示するなど、表示するメディアをデバイスに合わせて切り替えるという仕様に決まってしまうと簡単にはいきません。
単純にvideo要素とimg要素をCSSのメディアクエリーで表示・非表示の制御すれば、表面的には仕様に合った実装になります。
この場合、CSSでスタイルをdisplay: none
と指定して要素を非表示にするのですが、要素そのものはHTMLから消えないため、video要素で指定された動画ファイルがダウンロードされてしまいます。
使わない動画をわざわざダウンロードするというのはもったいない話です。
そこで、JavaScriptの出番です。
今回は、JavaScriptを使って、動画を表示して再生するときだけ、動画ファイルがダウンロードされるように実装する手順を解説します。
処理の流れ
メソッドを定義してすぐ実行されるように、即時実行関数式(IIFE) を定義します。
そして、画面幅の変動を検知するために、MediaQueryListオブジェクトを生成します
以下の2つのイベントにリスナーを設定します。
- HTMLの読み込みと解析が完了したとき
- メディアクエリーの状態が変化したとき(画面幅が変更されたとき)
動画を再生する状況であれば、CSSでvideo要素を表示して、JavaScriptで動画をするメソッドを定義します。
HTMLについて
今回、動画と画像のHTMLは以下のようにしました。
<div class="video-wrap">
<video id="videoElement" data-src="mv01.mp4" muted autoplay playsinline autoplay>
</div>
<div class="image-wrap">
<img src="image01.jpg" alt="">
</div>
それぞれの要素について、簡単に説明します。
video要素
<div class="video-wrap">
<video id="videoElement" data-src="mv01.mp4" muted autoplay playsinline autoplay>
</div>
今回は、動画が自動再生されるようにvideo要素の属性を設定しています。
video要素のsrc属性は、動画ファイルへのパスを設定すると動画ファイルがダウンロードされるため、省略しています。
代わりに動画ファイルへのパスは、カスタムデータ属性のdata-src属性に設定しておきます。
video要素を囲むdiv要素は、CSSのメディアクエリーで表示・非表示を指定するための要素です。
img要素
<div class="image-wrap">
<img src="image01.jpg" alt="">
</div>
今回は画像の表示・非表示をCSSのみで行います。
video要素を囲むdiv要素に対して、CSSのメディアクエリーで表示・非表示を制御します。
CSSについて
動画と画像を表示を切り替えるCSSは、以下のようにしました。
@media screen and (min-width: 601px) {
.video-wrap {
display: block;
}
.image-wrap {
display: none;
}
}
@media screen and (max-width: 600px) {
.video-wrap {
display: none;
}
.image-wrap {
display: block;
}
}
画面幅が600pxより大きければvideo要素を囲むdiv要素を表示して、画面幅が600px以下だったらimg要素を囲むdiv要素を表示するスタイルにしています。
処理の説明
STEP1 即時実行関数式の定義とMediaQueryListオブジェクトを作成する
即時実行関数式を定義する
今回のJavaScriptは、ページが読み込まれると必ず実行されるメソッドになるので、即時実行関数式(IIFE)で定義します。
// 即時実行関数式で定義する
(function(){
// ここに処理を書く
})();
これで、今回作成するメソッドが他のJavaScriptに影響しないようにしつつ、メソッドを定義してすぐ実行されるようになります。
MediaQueryListオブジェクトを作成する
ブラウザの画面幅を判別して、動画を再生するか静止画を表示するかを決めるため、MediaQueryList
オブジェクトが必要になります。
MediaQueryList
は matchMedia()
をwindow
オブジェクト上で呼び出して作成します。
(function(){
// MediaQueryListオブジェクトを生成する
const mql = window.matchMedia('(min-width: 601px)');
})();
今回は、600pxより大きい画面幅のときに動画を再生するので、メディアクエリー文字列を(min-width: 601px)
と設定しました。
これで、ブラウザの画面幅が601px以上であれば、mql.matches
プロパティがtrue
に設定されるため、画面幅の状態を判定することに使えます。
STEP2 イベントリスナーを設定する
次に、イベントリスナーを設定します。
今回検知したいのは、「ページの読み込みが完了したとき」と「画面幅が変更されたとき」に発生するイベントになります。
DOMContentLoadedイベントにリスナーを設定する
今回は、画像や動画などの読み込みを待つ必要がないため、HTMLの読み込みと解析が完了したあとで発生するイベントのDOMContentLoaded
にaddEventListener()
メソッドを設定します。
(function(){
const mql = window.matchMedia('(min-width: 601px)');
// DOMContentLoadedイベントにリスナーを設定する
document.addEventListener('DOMContentLoaded', setVideoSrc);
})();
MediaQueryListのchangeイベントにリスナーを設定する
画面幅が変わったときに処理が実行されるように、MediaQueryList
オブジェクトのchange
イベントに addEventListener()
メソッドを設定します。
(function(){
const mql = window.matchMedia('(min-width: 601px)');
document.addEventListener('DOMContentLoaded', setVideoSrc);
// MediaQueryListオブジェクトのchangeイベントにリスナーを設定する
mql.addEventListener('change', setVideoSrc);
})();
イベントリスナーに無名関数を使わない理由
ちなみにイベントリスナーにメソッドを設定せずに、以下のように無名関数で処理を実行させることもできます。
// イベントリスナーに無名関数を設定する
document.addEventListener('DOMContentLoaded', function(){
// ここに処理を書く
});
今回はメディアクエリーが変わったときにも同じ処理を実行させたいので、無名関数ではなく新しくメソッドを定義して、それぞれのイベントリスナーから呼び出されるようにします。
こうすることで、変更があっても1箇所だけ直せば良く、効率良くコードを書くことができます。
STEP3 video要素を再生するメソッドを作成する
メソッドを定義する
動画再生を制御するメソッドとして、setVideoSrc()
を定義します。
(function(){
const mql = window.matchMedia('(min-width: 601px)');
// 動画再生を制御するメソッド
function setVideoSrc(){
}
document.addEventListener('DOMContentLoaded', setVideoSrc);
mql.addEventListener('change', setVideoSrc);
})();
video要素を取得する
video要素を取得して、変数に入れます。
video要素にはid属性を設定しているので、getElementById()
メソッドで取得できます。
function setVideoSrc(){
// video要素を取得する
const element = document.getElementById('videoElement');
}
指定したメディアクエリーと一致しているか判定する
指定したメディアクエリー(601px以上)と一致しているか判定します。
判定には、 MediaQueryList
オブジェクト mql
の matches
プロパティの値を使います。
function setVideoSrc(){
const element = document.getElementById('videoElement');
// 指定したメディアクエリーと一致しているか判定する
if (mql.matches) {
// 動画を再生する処理
}
}
この値がtrue
であれば、601px以上の画面幅になっていということなので、動画を再生する処理を実行させます。
video要素のsrc属性に値を設定する
動画ファイルのダウンロードが行われないように、video要素のsrc属性は空にして、ファイルへのパスはカスタムデータ属性のdata-src属性に設定してあります。
この data-src属性の値をsrc属性に設定して、動画ファイルが読み込まれるように準備します。
カスタムデータ属性の値は、dataset
プロパティで取得できます。
function setVideoSrc(){
const element = document.getElementById('videoElement');
if (mql.matches) {
// カスタムデータ属性の値をsrc属性に設定する
element.src = element.dataset.src;
}
}
video要素にload()メソッドを実行する
video要素のsrc属性に動画ファイルのパスを設定しただけでは、動画が再生されません。
video要素を初期状態にリセットして、動画ファイルの読み込みを開始するためにload()
メソッドを実行します。
function setVideoSrc(){
const element = document.getElementById('videoElement');
if (mql.matches) {
element.src = element.dataset.src;
// video要素をリセットして、動画ファイルの読み込みを開始する
element.load();
}
}
load()
メソッドは、MDNのHTMLMediaElement.load()で詳しく解説されています。
これで、機能を実装することができました。
完成したコード
完成したJavaScriptのコードは、以下のようになります。
// 即時実行関数式で定義する
(function(){
// MediaQueryListオブジェクトを生成する
const mql = window.matchMedia('(min-width: 601px)');
// 動画再生を制御するメソッド
function setVideoSrc(){
// video要素を取得する
const element = document.getElementById('videoElement');
if (mql.matches) {
// カスタムデータ属性の値をsrc属性に設定する
element.src = element.dataset.src;
// video要素をリセットして、動画ファイルの読み込みを開始する
element.load();
}
}
// DOMContentLoadedイベントにリスナーを設定する
document.addEventListener('DOMContentLoaded', setVideoSrc);
// MediaQueryListオブジェクトのchangeイベントにリスナーを設定する
mql.addEventListener('change', setVideoSrc);
})();
デモ
実際にどんな風に動作するのか、確認するためのデモページを用意しました。
以下のリンクから見ることができます。
https://tech-blog.tomono.jp/demo/switch-according-to-the-situation/
まとめ
今回は、画面幅に合わせて動画再生・静止画像表示を切り替えるようにしましたが、特定の条件で再生する動画を切り替えたり、静止画の方もダウンロードさせないようにsrc属性をJavaScriptで設定するなど、いろいろ応用できると思います。
動画はどうしてもファイル容量が大きいため、ダウンロードに時間がかかったり、デバイスに負荷がかかったり、扱いにまだ困るケースが出てしまいます。
ユーザーによい体験をしてもらうためにも、細かいところまで気を遣って、工夫していきましょう。