ナム山

家最高 今年一年でサックスが吹けるようになるか観測中

firefoxで動画が再生されない - 変数の巻き上げと処理順序 -

症状

Firefoxでのみ、JavaScriptからprependしているvideoタグが自動再生されない。

原因

  • videoタグ自体の不具合でなく、自動再生の関数内で変数がundefinedになる。
  • 自動再生処理がload終了後にちゃんと走っていなかった。

処理順序は以下
ready内でvideoタグをprepend、その後load内で自動再生の関数
このように切り分けていたが、ページ遷移してきた時に処理順序が担保されていなかった。(先に自動再生の関数が叩かれていた)
後述の背景でブラウザごとに処理を分けたせいではなく、そもそも描画と再生段階でエラーを吐いているようだったので原因は以下だと切り分けて進めた。

  • コールバック処理しきれていなかった
  • さらに変数の巻き上げ

背景と仕様

以前、Chromeのポリシーの変更の際videoタグで自動再生が動かなくなった。
案件の内容に関わるので詳細は省くが、さらにChromeとそれ以外で分けたい処理があったので条件分岐していた。

  1. 「ブラウザ」「UA」で処理を分岐させる
  2. jsで条件にあったvideoタグを書き出す

以上が仕様。
ソース上にvideoタグがないため、描画→自動再生、と順序だって処理させる必要があった。
以前までは全ブラウザ問題なく動いているようだったが今回firefoxでうまく動かなかった。

対策

  • 関数化して処理順序を担保した
  • グローバル関数化して巻き上げ対策した

コード

■旧js

$(function() {
	if(Chromeだった場合 かつ PC幅だった場合) {
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//本当はブラウザ別の処理が書いてある
    }elseif(Chrome以外 かつ PC幅だった場合){
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//こちらもブラウザ別の処理が書いてある
    }
});

$(window).on('load',function(){
    if( PC幅 ){
        var v = $('#video').get(0);
        v.play();
        //さらにオプションなど
    }
});

■新js

$(function() {
	if(Chromeだった場合 かつ PC幅だった場合) {
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//本当はブラウザ別の処理が書いてある
		videoStart();//関数化した
    }elseif(Chrome以外 かつ PC幅だった場合){
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//こちらもブラウザ別の処理が書いてある
		videoStart();//関数化した
    }
});

function videoStart(){
    if( w >= 768 && flg == 'off' && ua.indexOf('iPad') == -1 ){
        v = $('#video').get(0); //グローバルに
        v.play();
    }
    return this;
}
巻き上げとコールバック

エラーは大きく2つ

  • 変数が未定義と怒られる
  • prependされる前に動画を再生しようとして行方不明になる

関数化してコールバック
先にコールバックについて、function(ready)とloadで分けていたので基本的には担保されていたはず。
しかし時と場合によっては順次処理がうまくいかず先に自動再生のブロックまで進んでしまいエラーになるようだった。
そのため関数化して明確にロード処理の終わりで叩かせることに。
巻き上げ
v = undefinded で怒られていた。
変数の巻き上げと言ってしまうと正確ではないが、宣言しているはずなのに未定義と怒られるケースから、変数の巻き上げ対策を取ることにした。
そのため根本的な解決ではないが、グローバル関数化したら未定義で怒られることはなくなった。
qiita.com

明示的に関数の先頭で宣言しようということでグローバル変数で宣言した。
(2019.11.25追記)今考えるとprependを飛ばしてエラーが起きていたので、単純に処理順序が担保されれば自ずと解決したかもしれない...。

備考と感想

  • 一応サンプルコードなのでvとか簡単にしてますが変数名はもうちょっとちゃんとしてます
  • 定数でもよかったかも

JavaScript特有の巻き上げと、処理タイミング、という一番ありがちなコンボだったので原因の切り分けとデバッグまで1時間もかからなかった。
jQueryも3系なのでloadとreadyで明示的に動いてくれていたのであぐらをかいていた。
関数化する方がスマートといえばスマートなので、これからは意識して綺麗なコードにしようと思った。