自作のグリモンの処理をAutoPagerizeで動的に読み込んだページにも適用する方法(追記:もうちょっと詳細に書いてみた)

※この記事にはちょっとした誤りが含まれています。追記:2009.07.24(2)を参照してください。

このテの情報があまり出回ってないようなので書いておく。

AutoPagerizeには、その動的に読み込んだページを継ぎ足すときに、特定の処理をそのページに対して適用する仕組みがちゃんと用意されている。

先日公開したグリモン jaro.user.js でもその仕組みを利用している。

  if (window.AutoPagerize) {
    window.AutoPagerize.addDocumentFilter(function(doc) {
        setOpacity(sites, doc);
    });
  }

上記のように、AutoPagerize.addDocumentFilterに特定の処理を行う関数を渡してあげると、動的に読み込んだページを継ぎ足すときにその関数を実行してくれるようになる。

もうちょっと詳細に解説してみる...

という感じで書いてたら、よくわからないという声が聞こえてきたので、もうちょっと詳しく解説してみる。(自分の理解も確かめる意味で)


たいていグリモンでやることと言ったら、ページを読み込んだタイミングでXPathやDOMのAPIで目的の要素を探して、その要素を書き換えたり要素を追加したりとかという感じだと思う。

ただし、AutoPagerizeを使っている場合、ページの読み込みはXMLHttpRequestによってバックグラウンドで行われ、通常のページの読み込みのようなイベントが発生しない。そのため、継ぎ足されたページ以降に対して処理が行われないことになってしまう。

意外と知られていないが、AutoPagerizeではそのように継ぎ足されるページに対する処理を外部のスクリプトからプラガブルに追加する仕組みが用意されている。

それが、先に挙げたAutoPagerize.addDocumentFilterである。

先日公開したグリモン jaro.user.js でもその仕組みを利用している。(以下コードの一部を抜粋。コードの全体はgist: 149474 - GitHubを参照)

たとえば、通常のページ読み込み時に実行される処理は以下のようにGM_xmlHttpRequest関数のonload:の処理として記述している。

    GM_xmlhttpRequest({
      method: 'GET',
      url: 'http://wedata.net/databases/SPAM_Sites/items.json',
      onload: function(res) {
        try {
          sites = [i.data.url_pattern for each(i in eval(res.responseText))];
          setOpacity(sites);
          storeCache(sites);
        } catch(e) { }
      }
    });

で、そこからこのグリモンの中心的な処理であるsetOpacityを呼び出している。そのsetOpacityでは...

  function setOpacity(sites, doc) {
    mergedSites = sites.concat(local_setting);
    $X('//a[@class="l"]', doc).forEach(function(ele) {
      if (isExcepts(ele.href)) {
        return;
      }
      if (isTarget(ele.href, mergedSites)) {
        ele.parentNode.parentNode.style.opacity = '0.3';
      }
    });
  }

上記のように、$X関数を使ってページ内から目的の要素を抜き出して透明度(opacity)の設定を行うなどの操作を行っている。ここまでは普通だと思う。

じゃあ、これと同じことをAutoPagerizeで動的に継ぎ足したページにも適用したいんだけど、どうしたらいいのか?
それを行っているのが以下の行。

    window.AutoPagerize.addDocumentFilter(function(doc) {
        setOpacity(sites, doc);
    });

上記のように、window.AutoPagerize.addDocumentFilterに特定の処理を行う関数を渡してあげるだけで良い。
こうしてあげるだけで、動的に読み込んだページを継ぎ足す前にその関数を実行してくれるようになる。

そして、その関数に対して渡される値は以下の3つ。

  • 継ぎ足されるページのDOMオブジェクト
  • 継ぎ足されるページのURL
  • infoオブジェクト (?ごめんなさい良く分からなかった...)

今回のjaro.user.jsで必要なのは継ぎ足されるページのDOMオブジェクトだけだったので、それだけを受け取るような書き方をしている。

で、ここでまたsetOpacityを見てみる

  function setOpacity(sites, doc) {
    mergedSites = sites.concat(local_setting);
    $X('//a[@class="l"]', doc).forEach(function(ele) {

ここでのポイントは、第2引数としてDOMオブジェクトを受け取るようにしていること。
最初のページ読み込み時のsetOpacityの呼び出しでは第1引数のsitesしか渡していない。
しかし、2ページ目以降を継ぎ足すタイミングでは第2引数を渡すようにしている。

こうすることで何が起きるかと言うと、最初の呼び出し(第2引数が省略)では、現在開いているページを対象としてXPathの評価を行うが、2ページ目以降については、渡されたDOMオブジェクトを対象としてXPathの評価を行うという動きとなる。(このへんの動きについては$X関数のコードを読んでほしい)

以上のようすることで、AutoPagerizeで動的に読み込んだページに対しても1ページ目と同様に処理を適用することができる。

以上のような解説でどうでしょ?

追記:2009.07.24

コメントもらいました。id:os0xによる素晴らしいまとめがありました。

最初からここ↑を読んでおけば良かった。

追記:2009.07.24(2)

上記リンクの内容を確認してみると自分の記事に誤りがあることがわかった。
addDocumentFilterではなく、addFilterを使うべきらしい。