Greasemonkey で jQuery を使うための覚え書き

もはや jQuery なしでは JavaScript が書けない状態なんだけど、Greasemonkey で使うためには一工夫必要だったのでメモしておく。
参考にしたところ

方針

  • HTMLにscriptタグを埋め込まない。
  • ページ表示時に毎回ロードするのは手間なので、ローカルにスクリプトを保存する。
  • unsafeWindow.$じゃなくて、$だけで使えるようにする。

コード

実際のコードはこんな感じ。
loadJQueryを呼び出すところは任意のイベントで構わない。

(function() {

const DEBUG = true;
const KEY_JQUERY = "my_jquery";
var $ = "shortcut";

document.onload = function() {
  loadJQuery(initialize);
}

function loadJQuery(f_initialize) {
  if (eval('unsafeWindow.$')) {//ロード済みなら初期化処理へ
    f_initialize();
    return;
  }

  var permanent = GM_getValue(KEY_JQUERY);
  if( typeof permanent == "undefined" ) {//ローカルに保存してなければダウンロード
    GM_xmlhttpRequest({
      method: 'get',
      url: 'http://jqueryjs.googlecode.com/files/jquery-1.1.3.1.js',
      onload: function(req){
        GM_setValue( KEY_JQUERY, encodeURI(req.responseText) );
        unsafeWindow.eval( req.responseText );
        f_initialize();
      },
      onerror: function(req) {
        log("Request for jquery resulted in error code: " + req.status);
      }
    });
  } else {//ローカルから取得
    unsafeWindow.eval( decodeURI(permanent) );
    f_initialize();
  }
}
function log(message) {
  if (unsafeWindow && unsafeWindow.console && DEBUG) {
    unsafeWindow.console.log(message);
  }
}
function initialize() {
  $ = unsafeWindow.$;
  //本来やりたい処理を書く
}
})();

注意点とか

上のコードで jQuery は使えるけど、いつも通りにはいかない。

  • jQuery.attrが使えない。値の取得も設定もエラーが発生する。
  • jQuery.valで値を設定しようとしても同じ。取得はできた。
  • $.ajaxも使えない(何が起こるかは忘れた)。

$.ajaxはGM_xmlhttpRequestでいいんだけど、jQuery.attrとjQuery.valはかなり痛い。確認してないけど、多分jQuery.textも同じだろう。

jQuery.attrの方は、name属性で対象要素を区別したかっただけなのでこんな感じで回避した。

var target = $('.hoge[@name=fuga]');

jQuery.valの方は、jQuery.eachの引数にDOM要素が渡されるのを利用した。

$('.hoge').each(function() {
  this.value = 'hogehoge';
});


あと、Greasemonkey 特有ではないけど、IDでjQueryオブジェクトを取得する場合に少し困った。
IDが重複する場合、最初に見つかったDOM要素しか返ってこないっぽい。ID本来の意味からいうと、動作的には正しいんだけど、ユニークになってないページも多いので困った。(GmailとかGmailとかGmailとか)

なので、対象となるDOM要素すべてを処理したい場合はこんな感じにした。

$('div[@id=duplicated]').each(function(){
  alert($(this).text());
});