#081
posted on 2022.09.30 (Fri)

(ネイティブJavaScript版) video要素にマウスオンで動画を自動再生。

HTMLのvideo要素でmp4などの動画ファイルをサイトに埋め込み表示させているとき、動画上にマウスオンで自動再生させたかったので、マウスオーバーで再生、マウスアウトで停止させる方法のメモ。

※ jQueryを使った方法は別記事を参照。

※ HTMLのvideo要素で動画ファイルをサイトに埋め込み表示する方法は前の記事を参照。

 

 

HTMLのマークアップ

マウスオンの判定領域となる親要素と、その中に「video」要素を入れ子にして記述。

  1. JavaScriptでのターゲット要素の判別用に任意のカスタムdata属性を付与。(ここでは「data-ag2video」。)
  2. 「video」要素に「muted」属性を付与。(Chromeなどでは消音状態でないと自動再生できない仕様なので。)
<div class="ag2video" data-ag2video>
	<video src="movie/foo.mp4" muted="true"></video>
</div>

 

 

JavaScriptで実装

マウスイベントでマウスポインタの動きを捕捉し、親要素の領域への流入・流出を判別し、「HTMLMediaElement」が持つJavaScriptのメソッドで動画の再生・停止を実行する。

 

[ 使用するマウスイベント ]

  • 「mouseover」、「mouseenter」 イベント : 指定した要素の領域へのマウスの流入で発火。
  • 「mouseout」、「mouseleave」 イベント : 指定した要素の領域からのマウスの流出で発火。

※ 「mouseover」、「mouseout」はバブリングするので、documentだけにリスナー登録をしておけばよいが、イベントが発火しているターゲット要素を常に確認して処理する必要がある。

※ 「mouseenter」、「mouseleave」はバブリングしないので、リスナー登録した要素でのみ発火するが、処理を実行したい要素すべてにリスナー登録しておく必要がある。(イベントターゲットは常に「this」と同じ。)

※ 「mouseover」、「mouseout」イベントについては前の記事を参照。

 

[ HTMLMediaElementの主なメソッドとプロパティー ]

  • 再生 : 「play()」メソッドを実行。
  • 一時停止 : 「pause()」メソッドを実行。
  • 停止(頭出し) : 「pause()」メソッドを実行して、「currentTime」プロパティーに「0」を代入。(停止のメソッドは無い。)
  • 消音 : 「muted」プロパティーにBoolean値を代入。「true」で消音が有効、「false」で消音が無効になる。(設定音量の数値は「volume」プロパティーに保持されているので、「muted」プロパティーでは変化しない。)
  • 音量 : 「volume」プロパティーに「0」~「1」の数値を代入。(「0」は無音。)
  • 長さ : 「duration」プロパティーに動画の長さが保持されている。
  • 再生割合の経過 : 「timeupdate」イベントを捕捉して、「duration」プロパティーと「currentTime」プロパティーから算出する。

※ HTMLMediaElementのMozillaの公式ドキュメント

 

 

1. 「mouseover」、「mouseout」イベントを使って実装する場合

バブリングするので、処理を関数でまとめてdocumentにリスナー登録しておいて、発火したイベントターゲットを確認して必要なときだけ実行する。

  1. 処理に必要な「祖先要素(自身を含める)から指定のカスタムdata属性を持つ要素を取得するメソッド」と「指定のノードを子要素に持つかを調べるメソッド」を自作して、それぞれElementインターフェイスとNodeインターフェイスにプロトタイプで実装。(継承とプロトタイプチェーンのMozillaの公式ドキュメント。)
  2. マウスポインタがカスタムdata属性「data-ag2video」を持つ要素に流入した場合、その要素が内包する「video」要素を取得。
  3. 取得した「video」要素を「play()」メソッドで再生。
  4. マウスポインタが親要素から流出した場合、「video」要素を「pause()」メソッドで停止。

※ 指定した秒数から再生したい場合は、「video」要素の「currentTime」プロパティーに任意の秒数を設定。

※ 「poster」属性を設定していて、停止後に画像を再表示させたい場合は、「video」要素を「load()」メソッドで再読み込みする。

//祖先から指定data属性を持つ要素を取得するメソッドを作成
if(!Element.prototype.ag2closest){
  Element.prototype.ag2closest = function(d){
    let _el = this;
    do{
      if(_el.dataset[d] !== undefined) return _el;
      _el = _el.parentElement || _el.parentNode;
    }while(_el !== null && _el.nodeType === 1);
    return null;
  };
}
//指定ノードを子要素に持つか確認するメソッドを作成
if(!Node.prototype.ag2contains){
  Node.prototype.ag2contains = function(n){
    while(n !== null){
      if(n === this) return true;
      n = n.parentElement || n.parentNode;
    }
    return false;
  };
}

//初期設定値
const ag2videoSettings = {
  dataName: 'ag2video', //親要素に付与してあるdata属性名
  classActive: 'ag2videoOn', //マウスオンで付与するクラス名
};
//現在再生中のvideo要素を保持する変数
let currentVideo = null;
const ag2video = {
  on: function(t){
    //クラスを付与
    t.classList.add(ag2videoSettings.classActive);
    //現在のvideo要素を代入して保持
    currentVideo = t.querySelector('video');

    //頭に戻す場合
    // currentVideo.currentTime = 0;
    //再生
    currentVideo.play();

    currentVideo.addEventListener('timeupdate', function(){
      ag2video.progerss(t);
    });
  },
  off: function(t){
    //停止
    currentVideo.pause();
    //posterを再表示させる場合(リロード)
    // currentVideo.load();

    currentVideo.removeEventListener('timeupdate', function(){
      ag2video.progerss(t);
    });

    //クラスを削除
    t.classList.remove(ag2videoSettings.classActive);
    currentVideo = null;
  },
  progerss: function(t){ //プログレスバー
    if(t.classList.contains(ag2videoSettings.classActive)) return;
    let percent = Math.floor(currentVideo.currentTime / currentVideo.duration * 1000) / 10;
    console.log(percent+'%');
  }
};

document.addEventListener('mouseover', function(){
  //ターゲット(またはその親要素)が「data-ag2video」属性を持っていて、かつ現表示の要素ではない場合
  let videoTarget = event.target.ag2closest(ag2videoSettings.dataName);
  if(videoTarget && videoTarget !== currentVideo){
    ag2video.on(videoTarget);
  }
});
document.addEventListener('mouseout', function(){
  //再生中の場合
  if(currentVideo){
    //移動先がブラウザウィンドウ外ではなく、かつ以下のどちらかの場合はreturn
    //移動元が現表示の親要素、かつ移動先がその子要素の場合
    //移動元が現表示の親要素の子要素、かつ移動先が現表示の親要素(またはその子要素)の場合
    if( event.relatedTarget && ((event.target === currentVideo && event.target.ag2contains(event.relatedTarget)) || (currentVideo.ag2contains(event.target) && currentVideo === event.relatedTarget.ag2closest(ag2videoSettings.dataName))) ) return;

    let videoTarget = event.target.ag2closest(ag2videoSettings.dataName);
    ag2video.off(videoTarget);
  }
});

 

 

2. 「mouseenter」、「mouseleave」イベントを使って実装する場合

「mouseenter」、「mouseleave」はイベントバブリングしないので、発火したイベントターゲットの確認処理を省けるが、すべての対象要素にリスナー登録しなければいけないので、対象となる要素をJavaScriptでまとめて取得できるように任意のクラス名を付与しておく必要がある。

//対象セレクターのNodelistを取得してすべてにリスナー登録
const ag2videos = document.querySelectorAll('.ag2video'),
      ag2videosNum = ag2videos.length;
for(let i = 0; i < ag2videosNum; i++){
  ag2videos[i].addEventListener('mouseenter', ag2video.on);
  ag2videos[i].addEventListener('mouseleave', ag2video.off);
};

 

 

この記事のURL

https://memo.ag2works.tokyo/post-5019/

Copyコピー
この記事のタイトル

(ネイティブJavaScript版) video要素にマウスオンで動画を自動再生。 | memo メモ [AG2WORKS]

Copyコピー
この記事のリンクタグ

<a href="https://memo.ag2works.tokyo/post-5019/" target="_blank" rel="noopener">(ネイティブJavaScript版) video要素にマウスオンで動画を自動再生。 | memo メモ [AG2WORKS]</a>

Copyコピー
※ フィールドをクリックでコピーするテキストの編集ができます。

この記事へのコメント

コメントの書き込みはまだありません。

  • コメント内のタグはエスケープ処理され、文字列として出力されます。
  • セキュリティーのため、投稿者のIPアドレスは取得されます。
  • 管理者が内容を不適切と判断したコメントは削除されます。
  • このフォームにはスパム対策として、Googleの提供するreCAPTCHAシステムが導入されています。
    (Google Privacy Policy and Terms of Service.)