💨

Blazer でYoutube Player APIを使ってYoutubeプレイヤーを表示してない別ページに切り替えるとエラーが起きた話

2021/12/11に公開

概要

最近Blazorを触っているのですが、タイトル通り、Youtubeプレイヤーを表示しているページからYoutubeプレイヤーを表示してない別ページに切り替えたときに起きた不具合の解決方法が調べても全然出てこなかったので、忘備録として記事にさせていただきました。

環境

  • Blazor WebAssemly
  • .NET SDK 6.0

Youtubeプレイヤーを表示しているページから別のページを切り替えるときにエラーが出る

Blazorは新規作成でプロジェクトを作ると、デフォルトで左側にサブメニューがあり、そこを押すとページが切り替わるようになっています。
ここでiframeを使ってYoutubeプレイヤーを表示したページからYoutubeプレイヤーがない別のページに切り替えた時に以下のエラーが出るようになりました。

調べたところ、どうやらDOM関連で空の要素をremoveChildを呼んでページの情報から削ぎ落とそうとしてるのはわかり、エラー文でググっても似たような現象に遭遇した人がいたのですが、解決には至りませんでした。
https://stackoverflow.com/questions/60183421/blazor-typeerror-cannot-read-property-removechild-of-null-at-object-e-as-rem

Youtubeプレイヤーを描画している「Youtube Player API」側で何か対処する必要があるのか調べたところ、destroy()という関数があるので、それを呼んだら解決しました。
https://developers.google.com/youtube/iframe_api_reference?hl=ja

player.destroy():Void
プレーヤーを含む <iframe> を削除します。

//破棄関数を用意して呼び出したら解決
window.finalizeYoutubePlayer() = function(){
	player.destroy();
}

別ページから戻ってきたらYoutubeプレイヤーが表示されなくなった

これで解決した…!と安心していたのですが、デバッグしていたらYoutubeプレイヤーを表示してない別ページからYoutubeプレイヤーを使っているページに戻ってきた時にYoutubeプレイヤーが表示されなくなりました。

調べたところ、どうやらplayer.destroyしたらYoutubeプレイヤーを再び設定しないといけないみたいでした。
こちらは公式で解説されているhtml内にYoutubeプレイヤーを挿入するJavascriptのコードです。

    var firstScriptTags = document.getElementsByTagName('script');
    var tag = document.createElement('script');
    tag.src = 'https://www.youtube.com/iframe_api';
    var firstScriptTag = firstScriptTags[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

こちらの処理を読ぶと、Youtubeプレイヤーの処理が記述されているスクリプトの読み込みが開始されます。
スクリプトが無事読み込まれるとonYouTubeIframeAPIReady()というコールバックでYoutubeプレイヤーが生成できるようになるので、そこでYoutubeプレイヤーを生成します。

てっきり、destroyをした後ももう1回上記の処理を呼び直し、コールバックを呼び出されるのを待機していればいいのかなと思っていましたが、どうやら、一度読み込まれたスクリプトは再度読み込み処理が走らないみたいで、onYouTubeIframeAPIReady()はコールバックとしては再度呼ばれないみたいでした。

こちらに関しては初めてYoutubeプレイヤー読み込んだタイミングでonYouTubeIframeAPIReady()はすでに呼ばれており、スクリプト自体は破棄されているわけではないので、コールバックを待つんじゃなくて再度関数を直接呼び出せばいいのでは?と思い呼び出したら解決しました。

window.initializeYouTubePlayer = function () {
       //追加
       //一度は生成されているのでインスタンスはいるはず
            if(player){
                if(!player.getIframe()){
                    window.onYouTubeIframeAPIReady();
                }
		//既にscriptは読み込み済みなので何もせず返す
                return;
            }
	    //追加終了

            var firstScriptTags = document.getElementsByTagName('script');
            var tag = document.createElement('script');
            tag.src = 'https://www.youtube.com/iframe_api';
            var firstScriptTag = firstScriptTags[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        }

終わりに

同じような現象にあった人の解決になれば幸いです。

Discussion