自分でAutoPagerize対応のスクリプトを書く簡単な方法

ちょっとアレなタイトルですが、GreasemonkeyAutoPagerize対応のスクリプトを自作する時の注意点を2つメモ。
自分はひよっこですが、これからGreasemonkeyスクリプト書いてみようかなという人の助けに少しでもなれば嬉しいです。

継ぎ足されたページに適用する方法

AutoPagerizeで継ぎ足された部分に自分のスクリプトを適用する方法あれこれ - 0xFF
を参考に、AutoPagerize_DOMNodeInsertedを使うことにする。
以前はaddFilterとかGM_AutoPagerizeLoadedとか出てくる書き方をしていたんだけれど、この方法がやはり簡単・シンプルなので採用させて頂きました。

継ぎ足されたページ「のみに」適用する方法

新しく継ぎ足されたページのみに、スクリプトの内容を適用・反映したい場合。
注意しないと、継ぎ足されたページのみに適用しているように見えても実は1ページ目から全部無駄に作業を繰り返してしまう場合がある。


以下はgetElementByIdとかを使う場合は気にしなくて良い話なのだけど、
XPathを使う人(document.evaluateを使う人)には大きな注意点。
一言で言うと、document.evaluateを使うときにはXPathの指定で先頭にドットを付けるようにする。
つまり"//div"ではなくて".//div"とする。
そうしないと「継ぎ足されたページのみ適用」はできない。


次のページに紹介してある。


第2引数の指定を生かすには

* 頭に.をつける
* 頭に/をつけずに、decendant::ほかを使う

などする。

以上をまとめると

(function() {
    //各ページで実行したい内容
    function handle(node){
        //エレメント一個を扱う場合
        var item = document.evaluate('.//ここにXPathを記入',node,null,7,null).snapshotItem(0); 

        //複数のエレメントを扱う場合
        var items = document.evaluate('.//ここにXPathを記入',node,null,7,null);
        for (var i = 0; i < items.snapshotLength; i++)
        {
             item = items.snapshotItem(i);
        }
    }
    
    document.body.addEventListener('AutoPagerize_DOMNodeInserted',function(evt){
        var node = evt.target;
        //var requestURL = evt.newValue;
        //var parentNode = evt.relatedNode;
        handle(node);
    }, false);
    
    handle(document);
    
})();

というテンプレートをコピペして使っています。

追記

間違ったことを書いてしまったようですorz (現在は訂正済み)いないと思いますが10月12日21時以前に上のテンプレートをコピペして使った方がいらっしゃたらすみません。os0xさんありがとうございます!

追記終わり

実際にいろいろ使ってます。前書いた楽天でのお買い物を便利にするGreasemonkey3つ - bloooとかも使っています。一応問題なくうまく行っているようです。簡単なスクリプトならこれで書けると思います。
誤りがあればご指摘ください(ょょ

試してみる

ださい方法しか示せなくてすみません。
Google検索結果で上で書いた「ドットの有る無し」ひとつが重要なことを確認してみました。
「新しく読み込まれたページのみに適用する」「最初っからまた適用する」様子を実際に観察できます。


こういうときにテストがめんどくさいと絶対にやらないと思うのでインストールできるようにしておきました。
Test AutoPagerize on Google Search for Greasemonkey
Googleで適当に検索、スクロールしていって2ページ目をロードしたときに違いがわかります。


中身は以下のようになっています。

(function() {
    function handle(node){
        alert('new page loaded');
        var result = '';
        
        // ドット"."なし → 1ページ目から全部適用
        result += '(1) //a[@class="l"] (Without Dot)\n';
        var items = document.evaluate('//a[@class="l"]',node,null,7,null);
        for (var i = 0; i < items.snapshotLength; i++)
        {
           result += items.snapshotItem(i);
           result += '\n';
        }
        
        // ドット"."あり → 新しく読み込んだページのみ適用
        result += '\n(2) .//a[@class="l"] (With Dot)\n';
        var items = document.evaluate('.//a[@class="l"]',node,null,7,null);
        for (var i = 0; i < items.snapshotLength; i++)
        {
           result += items.snapshotItem(i);
           result += '\n';
        }
        
        alert(result);
    }
    
    document.body.addEventListener('AutoPagerize_DOMNodeInserted',function(evt){
        var node = evt.target;
        //var requestURL = evt.newValue;
        //var parentNode = evt.relatedNode;
        handle(node);
    }, false);
    handle(document);
})();

参考サイト2

  1. Latest topics > getElementsByなんちゃら の代わりにXPathを使う - outsider reflex
    • document.evaluateの使い方。getElementBy...よりとても便利。
  2. Latest topics > CSS3セレクタとXPathでの表現の対応表 - outsider reflex
    • よく参考にさせて頂いています。(これはAutoPagerizeとは関係無いか。Stylish書くときに参考にしています)
  3. Introduction to using XPath in JavaScript - MDC

追記

せっかくなので「昔のやってたやり方」も載せておきます。
でも上のやり方の方が好きです。

(function() {
    //各ページで実行したい内容
    function handle(node){
        //エレメント一個を扱う場合
        var item = document.evaluate('.//ここにXPathを記入',node,null,7,null).snapshotItem(0); 

        //複数のエレメントを扱う場合
        var items = document.evaluate('.//ここにXPathを記入',node,null,7,null);
        for (var i = 0; i < items.snapshotLength; i++)
        {
             item = items.snapshotItem(i);
        }
    }
    
    function registerPageHandler() {
        window.AutoPagerize.addFilter(function(pages) {
            pages.forEach(function(page) {
            handle(page);
        });
      });
    }
    
    if (window.AutoPagerize) {
        registerPageHandler();
    } else {
        window.addEventListener('GM_AutoPagerizeLoaded', registerPageHandler, false);
    }
    handle(document);
})();

これで多分動きます。

追記2

修正した形跡をここに残しておきます。

    if ("AutoPagerize" in window)
    {
        document.body.addEventListener('AutoPagerize_DOMNodeInserted',function(evt){
            var node = evt.target;
            //var requestURL = evt.newValue;
            //var parentNode = evt.relatedNode;
            handle(node);
        }, false);
     }

このif ("AutoPagerize" in window)を付けては、正常動作しないことがあるということを教えて頂きました。詳しくはコメント参照。ありがとうございます!

その2

馬鹿な間違いを発見 snapshotLength(0)とか...orz 修正しておきました。