ちょっと実行時に video タグを動的に追加する必要があったので、以下のようなコードを組んでいたのですが、何故か二重に動画が再生されてしまう問題が発生しました。
$("#wrap").append('<video src="kamebuchi.mp4" controls autoplay loop></video>');
ちょっと調べてみると jQuery の parseHTML 実装に原因があったようなので、簡単にまとめておきます。
まずは問題が発生したバージョンと発生しなかったバージョンの parseHTML 実装を調べてみました。
jQuery 1.8.2
// data: string of html // context (optional): If specified, the fragment will be created in this context, defaults to document // scripts (optional): If true, will include scripts passed in the html string parseHTML: function( data, context, scripts ) { var parsed; if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { scripts = context; context = 0; } context = context || document; // Single tag if ( (parsed = rsingleTag.exec( data )) ) { return [ context.createElement( parsed[1] ) ]; } parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); return jQuery.merge( [], (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); },
1.8.2 の実装では buildFragment が cacheable の値として、true を返した場合にはクローンするようになっています。video タグは DOM ツリーに追加されなくても音だけは再生される仕様なので、jQuery にキャッシュされた video タグが音だけ再生していたようです。
jQuery 2.1.1
// data: string of html // context (optional): If specified, the fragment will be created in this context, defaults to document // keepScripts (optional): If true, will include scripts passed in the html string jQuery.parseHTML = function( data, context, keepScripts ) { if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } context = context || document; var parsed = rsingleTag.exec( data ), scripts = !keepScripts && []; // Single tag if ( parsed ) { return [ context.createElement( parsed[1] ) ]; } parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts && scripts.length ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); };
ちなみに 2.1.1 の実装ではパースした結果をキャッシュする仕組みは無くなっています。
このように実装が変わったのは 1.9.0 からのようなので、新しい jQuery を使っている限りでは、二重に再生される問題は発生しないようになりました。どうしても新しい jQuery にアップデート出来ない場合には、createElement を使って昔ながらの方法で要素を作るしかなさそうです。
var video = document.createElement("video"); video.src = "kamebuchi.mp4"; video.controls = true; video.autoplay = true; video.loop = true; $("#wrap").append(video);
jQuery の parseHTML を通さないので、要素がキャッシュされず問題なく再生されるようになりました。