最近JavaScriptでHTMLVideoElement周りをゴリゴリ触っています。
そこで得た知見を共有できたらと思い記事として執筆しようと思います。
イベントハンドラとは?
まず始めに知っている方は飛ばしてください。
駆け出しエンジニアの方向けに説明します。
HTMLの要素に対して何かしらの操作が行われたときにはイベントが発生します。
例えばカーソルなどでクリックしたときにはclickというイベントがブラウザ内で発生しており、そういったイベントを検知して何かしらの処理を実行させる仕組みがイベントハンドラです。
何かしらの処理というのはプログラマ自身が関数として実装します。
この関数をコールバック関数と呼びます。
その関数をイベントリスナーというイベントを登録するためのメソッドがあり、そのメソッドの引数に渡すことで特定のイベントに対して実行させることができるようになるのです。
仕様が複雑化するとイベントハンドラにロジックを書くのはバグの温床になりやすい
例えば、
- 一時停止時にAの処理
- 一時停止から再生した際にはBの処理
- 終了時にはCの処理
- シーク操作で再生しながらシークした場合はDの処理
- 一時停止してからシーク操作した場合はEの処理
など操作毎に別々の処理を実行したいとなると、イベントハンドラに直接ロジックを書いていると悩ましい問題が発生します。
以下、ブラウザ毎の動画プレーヤー操作に対するイベント発生タイミングを簡単にまとめてみました。 (数字は順番で赤字の部分はブラウザ毎に差分がある箇所です。あくまで参考程度にとどめてください。)
若干safariが異なりますが、これを見ての通りブラウザ毎にイベント発生のタイミングが若干異なることと、シーク操作をしているのに内部ではpauseやplayなどのイベントが発生していることがわかるかと思います。
そのためイベントハンドラで処理していると思わぬタイミングで処理が走り不具合の原因となりかねません。
C.)を行いたいのに動画再生位置が末尾に達したときにC.)だけでなくA.)の処理も走ってしまったり、D.)を行いたいのにDの処理の前にA.)が行われ最後にB.)が行われてしまうといったことが起こりえます。
また、ブラウザ側の実装を使っていると再生中でのシークと一時停止中でのシーク操作を切り分けるのは難しいです。
掲載した表のとおりシーク時には一度動画を停めてから再生位置移動を行うためです。
さらにこの時直前が再生状態であれば自動でplayイベントが呼ばれます。
このようにイベントハンドラでは簡単な仕様には対応できるけど仕様が複雑化するとブラウザ側の仕様に引きづられて思うように実装できない上、思わぬところで不具合が起きてしまう可能性が出てくるのです。
解決方法はあるのか?
個人の見解として複雑な仕様で制限なしに機能実装を行いたい場合はブラウザの動画プレイヤーが提供するコントローラー(再生ボタンやシークバー、最大化ボタンがついてるやつです)を一切使わずに独自でUIを構築して再生ボタンをやシークバーを自前で作り、play()やpause()を使って動画プレイヤーを操作する方が良いです。
どうしてもブラウザのUIを使うと再生時に何かしらの処理を行いたい場合はplayイベントを観測してイベントが発生したらイベントハンドラを実行させるというような実装をする必要があり、そうなってくるとブラウザのイベントタイミングに依存してしまいます。
これらをうまく回避使用するならば内部で独自にフラグを持って制御したりと色々めんどくさいことになるため、以上のことから個人的には独自UIで再生ボタンをクリックしたらこの処理を行いplay()を呼び出して動画を再生するような実装の方がイベントタイミングに依存しない実装が実現できると考えています。
最後に
開発でHTMLVideoElement周りを触る場合、ある程度先のことを見据えて実装を行う必要があるなと感じました。
これは動画プレーヤーに限ったことではないですが、設計の重要さを改めて再認識しました。
以下にHTMLVideoElementを触る際に参照したサイトを掲載いたします。
仕様を把握して実装することは大事なので是非確認してみてください。
参考
HTMLVideoElement周りを色々いじる際には以下のサイトで仕様を確認したり、挙動を知ることをお勧めします
HTMLVideoElement仕様が記載されています https://html.spec.whatwg.org/multipage/media.html#the-video-element
HTMLVideoElementで発生するイベントの動きをブラウザコンソール上で確認できます メディア | JavaScriptのイベントをたくさん見られるサイト
HTMLVideoElementのイベントやプロパティが持つ状態を観測できます https://www.w3.org/2010/05/video/mediaevents.html