Hatena::ブログ(Diary)

blooo

2009-10-12

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

ちょっとアレなタイトルですが、GreasemonkeyでAutoPagerize対応のスクリプトを自作する時の注意点を2つメモ。

自分はひよっこですが、これからGreasemonkeyスクリプト書いてみようかなという人の助けに少しでもなれば嬉しいです。

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

AutoPagerizeで継ぎ足された部分に自分のスクリプトを適用する方法あれこれ - 0xFF

を参考に、AutoPagerize_DOMNodeInsertedを使うことにする。

以前はaddFilterとかGM_AutoPagerizeLoadedとか出てくる書き方をしていたんだけれど、この方法がやはり簡単・シンプルなので採用させて頂きました。

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

新しく継ぎ足されたページのみに、スクリプトの内容を適用・反映したい場合。

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


以下はgetElementByIdとかを使う場合は気にしなくて良い話なのだけど、

XPathを使う人(document.evaluateを使う人)には大きな注意点。

一言で言うと、document.evaluateを使うときにはXPathの指定で先頭にドットを付けるようにする。

つまり"//div"ではなくて".//div"とする。

そうしないと「継ぎ足されたページのみ適用」はできない。


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

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

* 頭に.をつける

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

などする。

JavaScript: document.evaluateの第2引数に特定のノードを指定したときは頭に/を使ってはいけない - mayokara note

以上をまとめると

(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);
})();

参考サイト

  1. JavaScript: document.evaluateの第2引数に特定のノードを指定したときは頭に/を使ってはいけない - mayokara note
  2. AutoPagerizeで継ぎ足された部分に自分のスクリプトを適用する方法あれこれ - 0xFF
  3. 自作のグリモンの処理をAutoPagerizeで動的に読み込んだページにも適用する方法(追記:もうちょっと詳細に書いてみた) - 今日もスミマセン。
    • 僕が始めに参考にさせて頂いたページです。
  4. AutoPagerizeのスクリプト実行順序制約をなくせるようになりました « ku
    • そのsnakaさんに教えて頂いたページ。
  5. 勝手に添削してみる (AutoPagerize対応GMScriptの書き方みたいな) - 0x廃棄階層 - 統治局
  6. AutoPagerize Wiki: APIs (ja)
  7. 自作greasemonkeyのテンプレートを公開 - かせいさんとこ

参考サイト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 修正しておきました。

os0xos0x 2009/10/12 20:34 if ("AutoPagerize" in window)
この条件がtrueになるのは、AutoPagerizeが先に読み込まれていた場合だけです。
Greasemonkeyではインストールした順番に実行されるので、最初はこれでうまくいきますが、AutoPagerizeをアップデートするとAutoPagerizeの実行順が後ろになるので、("AutoPagerize" in window)がfalseになってしまいます。
kuさんの「AutoPagerizeのスクリプト実行順序制約をなくせるようになりました」という記事はこの問題の解決策です。
で、AutoPagerize_DOMNodeInsertedを使うのであれば、if ("AutoPagerize" in window)をなくしてしまうだけでOKです。

bloooblooo 2009/10/12 20:48 コメントありがとうございます!こ、これは…致命的な欠陥では…
勉強不足の身でこんなエントリ書いてすみません。早速訂正しておきました。(他のスクリプトも直さねばorz)
本当にありがとうございます!

os0xos0x 2009/10/13 08:20 いえいえ、こうやってエントリを書くからわかることも多いですから、それほど気にすることはないと思います。
もちろん間違いがなければそれも素晴らしいですが、ミスは誰にでもあるので、それをちゃんと訂正すれば問題ないと思います。
また、こういうミスをしてて、こう直したっていうのも学習の材料として役に立ちます。

bloooblooo 2009/10/13 21:46 ありがとうございます。エントリ公開後まもないうちに教えて頂けたのが大変助かりました。
追記として訂正の形跡も残しておきました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

Connection: close