2013年2月24日日曜日

WordPressでjQueryの$が使えないって話を聞いて思ったこと

理由は解らないけど$って書くとエラーが出て、jQueryって書き直したら大丈夫だった
って話を小耳に挟みました。

これの意味するところは

window.jQueryは使えたけど、window.$は使えなかった
だってことを理解しないと、$が使えなかった理由も、jQueryと書き直して解決できた理由も謎のままなんだろうなと思いました。

そしてその謎が解ければ、$をjQueryに書き直す必要がなかったことも解るでしょう。

$が使えなかった理由

WordPressではjQuery.noConflict()が呼ばれているからです。

試しに次のようなHTMLを作って確認してみましょう。

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<script>
window.$ = 'test';
</script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
//jQuery.noConflict();
console.log(window.jQuery);
console.log(window.$);
</script>
</html>

jQuery.noConflict()によって、window.$が元の値(上の例では文字列'test')に戻ることが確認できると思います。
別のライブラリでなどでwindow.$を使っていて、jQueryによってwindow.$が別物に変わってしまうのを防ぐため、といった使い方が考えられるでしょう。

WordPressで$を使う方法

次のようなテキストウィジェットで、WordPressのJSでも$を使えることが確認できます。

<script>
console.log(window.jQuery);
console.log(window.$);
(function($) {
  // ここで$が使える
  console.log($.fn.jquery);
  console.log($('body'));
})(window.jQuery);
</script>

ポイントは、$を変数名として使うことも、それにwindow.jQueryを代入することも可能だという、すごく当たり前のことです。
使えなくなったのはwindow.$だけなのです。

そしてこのようにwindow.$を使わずに書いておくことで、jQuery.noConflict()の影響を受けないコードになっています。

jQuery.noConflict()を追ってみる

最後に、jQueryコアのソースから、noConflictに関係する箇所を抜粋しておきます。
変数_$に元のwindow.$を保持しておき、jQuery.noConflict()中で、window.$に_$を代入しているのが確認できます。

...
  // Map over jQuery in case of overwrite
  _jQuery = window.jQuery,

  // Map over the $ in case of overwrite
  _$ = window.$,
...
  noConflict: function( deep ) {
    if ( window.$ === jQuery ) {
      window.$ = _$;
    }

    if ( deep && window.jQuery === jQuery ) {
      window.jQuery = _jQuery;
    }

    return jQuery;
  },
...

興味深いのは、deepの指定です。
deep指定がなければwindow.$を元に戻すだけですが、deepをtrue指定すると、window.jQueryまで元に戻します。

先ほど、想定されるjQuery.noConflict()の利用局面は別のライブラリなどでwindow.$を使っている場合と書きましたが、deep指定が必要なのは、その別のライブラリがjQueryの別バージョンで、上書きされる前のwindow.jQueryにアクセスする必要がある場合です。

何年も前に書かれてメンテナンスが滞りがちなjQueryプラグインやウィジェットを使おうとすると、複数バージョンのjQueryを共存させる必要があるかもしれないですね。
紹介できそうな具体例を思いついたら、また書いてみたいと思います。