ページに複数のjQueryを読み込む。
どういう状況?
とあるWebサービスの、全てのページに入れるJS製共通コンポーネントを作成、反映することになった!
が、ページには既にjQueryが読み込まれ、且つjQueryプラグインが拡張されているような状態。順当な対応であれば、追加する共通コンポーネント側で、jQueryが読み込まれているかどうかを判定し、読み込まれていなければ読み込む、という対応になるだろうか。というかjQueryのバージョンくらい管理しろよという話でもあるけれども。
ググったら jQuery.noConflict(true)
なるもので、複数バージョンのjQueryを存在させておけるらしいが、挙動が色々不明瞭なので、色々検証してみた。
お題目
- ひとまず謎の
jQuery.noConflict(true)
を詠んでみる。 -
jQuery.noConflict(true)
を呼ぶ事で、呼ぶ前と何が変わるのか - どう使うべき?とう書くべき?
更なる検証として
- 最初に呼んだjQueryを拡張したプラグインの扱いはどうなるのか
- jQueryを3つ呼んだ場合
- jQueryを複数呼んでると思ったけど呼んでなくて、その状況で
jQuery.noConflict(true)
を呼んだ場合 - 同一バージョンのjQueryを複数呼んでいた場合
ひとまずjQueryオフィシャルを見てみる
jquery.noConflict のオフィシャルは ここ。
今回の件に関係がありそうな文面はというと…
If for some reason two versions of jQuery are loaded (which is not recommended), calling $.noConflict( true ) from the second version will return the globally scoped jQuery variables to those of the first version.
なんらかの理由により、2バージョンのjQueryが読み込まれ(それは非推奨だけど)、二つ目に呼んだjQueryから $.noConflict( true ) 呼ぶ事で、最初に呼んだ jQuery のグローバル変数のスコープを返します。
If necessary, you can free up the jQuery name as well by passing true as an argument to the method. This is rarely necessary, and if you must do this (for example, if you need to use multiple versions of the jQuery library on the same page), you need to consider that most plug-ins rely on the presence of the jQuery variable and may not operate correctly in this situation.
必要であれば、メソッドに引数として true を渡すことで、あなたは jQueryの名称(参照のことね)を自由にできる。これはレアケースだけど、必須で(例えば、もし複数のバージョンのjQueryを同一ページで利用する必要がある)、こういったケースでは、動かなくなるかもしれないjQueryプラグインの事を考慮する必要がある。
ここでの例は、
まずjquery-1.10.2.jsを読み込み、その後jquery-1.6.2.jsを読み込ませている。
$.fn.jquery
でバージョンをDOMに append
、その後、変数 jq162 に jQuery.noConflict(true)
を代入。最後に、$.fn.jquery
と、jq162.fn.jquery
を append
して結果、文字列 1.10.2 と 1.6.2 が出力されている。
これはつまり、jQuery.noConflict(true)
を呼んだ時点で、$ の参照先は最初に読み込まれた jQuery となり、jQuery.noConflict(true)
の戻り値に、二つ目に読み込まれた jQuery の参照が返るので、結果複数の jQuery が利用できる事となる。
ここまではOKなので、冒頭の項目に従って、更なる検証をしてみる。
最初に呼んだ jQuery を拡張したプラグインの扱いはどうなるのか
<script src="//code.jquery.com/jquery-1.6.2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
上の通り、まず jquery-1.6.2 を読み込み、jquery.easing を拡張。その後 jquery-1.10.2 を読み込む。
本来であれば、jquery-1.10.2 を読み込んだ時点で jquery-1.6.2 及び、jquery-1.6.2 に拡張された easing も存在しなくなるが、magic word jQuery.noConflict(true)
を呼んでみる。
上の記述に加え、以下の script を実行。結果は、easing が存在しないので、エラー(Uncaught TypeError: undefined is not a function)。
<script>
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
</script>
これを、以下のようにすると…
<script>
var jq162 = jQuery.noConflict(true);
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
</script>
動いた。
ということで、最初に読み込んだ jQuery を拡張したプラグインは、問題なく使える。
jQueryを3つ呼んだ場合
次に、jQuery を3つ呼んだ場合どうなるのかの検証。
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="//code.jquery.com/jquery-1.7.2.js"></script>
<script src="//raw.githubusercontent.com/kswedberg/jquery-smooth-scroll/master/jquery.smooth-scroll.js"></script>
<script src="//code.jquery.com/jquery-1.6.2.js"></script>
<script>
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
jQuery('<div id="hoge" style="padding-top: 2000px;"><a href="//google.com" id="google">google</a></div>').appendTo('body');
jQuery('<div id="fuga"><a href="#google" id="yahoo">yahoo</a></div>').prependTo('body');
jQUery.smoothScroll();
</script>
当然動かない。
一旦、一度だけ jQuery.noCnflict(true)
を呼んでみると、以下のように。
<script>
var jq000 = jQuery.noConflict(true);
console.log('jQuery = ' + jQuery.fn.jquery);
console.log('jq000 = ' + jq000.fn.jquery);
// jQuery = 1.7.2
// jq000 = 1.6.2
</script>
どうも挙動としては、一つ前に読み込まれた jQuery に変更されるということらしい。
じゃあその更に前の一番最初に読み込んだ jQuery はどうなのかということで、
<script>
var jq162 = jQuery.noConflict(true);
var jq999 = jQuery.noConflict(true);
console.log('jQuery = ' + jQuery.fn.jquery);
console.log('jq162 = ' + jq162.fn.jquery);
console.log('jq999 = ' + jq999.fn.jquery);
// jQuery = 1.10.2
// jq162 = 1.6.2
// jq999 = 1.7.2
</script>
も、戻っているゥゥゥ!というか戻せている!
というわけで、いくつ読み込んでも過去の jQuery は参照できる。
jQueryを複数呼んでると思ったけど呼んでなくて、その状況で jQuery.noConflict(true)を呼んだ場合
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<!--script src="//code.jquery.com/jquery-1.7.2.js"></script-->
<script>
var jq000 = jQuery.noConflict(true);
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
// X Uncaught TypeError: undefined is not a function
</script>
なんと、jQuery が存在しなくなってしまう。
上の例のように、戻り値を変数に代入しているような場合は、その変数に jQuery の参照が代入されるので、以下であれば動くが…
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<!--script src="//code.jquery.com/jquery-1.7.2.js"></script-->
<script>
var jq000 = jQuery.noConflict(true);
jq000('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jq000('#hoge').slideToggle(2000, 'easeOutExpo');
</script>
色々とよろしくないけれども、複数 jQuery があるかもしれないしないかもしれないような場合は、jQuery.noConflict(true)
の後に typeof jQuery
で jQuery の有無を調べ、ない場合は jQuery に 戻り値を代入するような事になるんでしょうかね。忍者やめろとカカシ先生に言われそうな、まったくけしからん状況ですが。
<script>
var jq000 = jQuery.noConflict(true);
jQuery = jQuery || jq000;
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
</script>
ちなみに戻り値の型は、戻す jQuery が存在する場合は function、戻す jQuery が存在しない場合は、undefined が返る。
同一バージョンのjQueryを複数呼んでいた場合
これは、特に問題なく動く。
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script>
var jq162 = jQuery.noConflict(true);
jQuery('<div id="hoge" style="font-size: 100px;">HOGE</div>').prependTo('body');
jQuery('#hoge').slideToggle(2000, 'easeOutExpo');
</script>
と、色々検証してみたけれども、一番良いのは、自分がマークアップしてるコンテンツの jQuery のバージョンは、なんとかきちんと管理しようぜという事なんじゃないかと。