Introduction

ブログ内検索

  • このサイトの記事を検索 by Google

おすすめの一冊!

無料ブログはココログ

« CSS ルールの動的な追加 | トップページ | 微妙な配列リテラル »

2011-05-05

iframe 要素の onload ハンドラ


開発中のブックマークレット「PruneBeforeClip」に
AutoPagerize みたいな機能を追加しようとしています。


ニュースサイトの記事で複数のページに分割されているものがあります。
最初のページだけクリップすると結論が書かれていないし、
途中のページだけクリップすると話の流れが把握できないし、
とにかくクリップして保存するには不便な状態になってしまっています。
ロイターには「記事を1ページに表示する」というリンクがあって
とても便利なのですが、他のサイトには残念ながらありません。

ところで、AutoPagerize という有名なユーザスクリプト (GreaseMonkey) があります。
Google の検索結果のページとかでページの末尾にスクロールすると、自動的に
次のページを読み込んで、ページ末尾に「継ぎ足して」くれるモノです。
Seaoak は使っていませんが、かなり便利だという評判。

で、考えてみると、ニュース記事の単一ページ化も、AutoPagerize と
同じ仕組みで実現可能なハズです。違いは、スクロールに関係なく
すべてのページを一気に展開してしまうところだけ。


とりあえず、実験ということで、あるニュースサイトで記事の単一ページ化が
できるようにしてみました。

処理はそんなに複雑ではなくて、次のような流れ:

  ・ページ内に「次のページ」や「前のページ」へのリンクがないかチェック。
  ・あったら、リンク先のページをバックグラウンドで読み込む。
  ・読み込んだページの本文部分を抜き出して、元のページのリンクの代わりに埋め込む。
  ・以上の処理をリンクがなくなるまで繰り返す。

「本文部分の抜き出し」はすでに PruneBeforeClip の基本機能として実装しているので、
問題になるのはリンク先のページの動的な読み込みです。

最初は XMLHttpRequest を使おうかと思ったのですが、読み込みたいのはテキストデータとか
XML ドキュメントではなく、ふつーの HTML ドキュメントなので、iframe を使うことにしました。

  ・iframe 要素を動的に生成して、元ページの DOM ツリーに追加する。
  ・iframe 要素の src にはリンク先の URL を指定する。
  ・iframe 要素の onload イベントハンドラで処理を行う。
  ・読み込んだドキュメントは iframe.contentDocument で参照できる
   (これをふつうの window.document と同じように扱えば OK)。

というわけで、出来たコードが以下のモノ:
function loadUrl(url, onload, onerror) { var iframe = document.createElement('iframe'); iframe.style.visible = 'hidden'; iframe.setAttribute('height', '0'); iframe.setAttribute('width', '0'); iframe.setAttribute('src', url); if (onload) { iframe.onload = function(event) { try { onload(iframe); } catch(e) { if (isDebug) alert('FATAL: ' + e); } }; } if (onerror) { iframe.onerror = function(event) { try { onerror(iframe); } catch(e) { if (isDebug) alert('FATAL: ' + e); } }; } document.body.appendChild(iframe); } (function() { var ctrlDom = document.getElementsByClassName('ctrl')[0]; var url = ctrlDom.getElementsByTagName('a')[0].href; loadUrl(url, function(iframe) { var target = iframe.contentDocument.getElementById('tmplBody'); if (! target) throw 'unexpected content'; removeGarbage(target); arrayEach(function(elem) { ctrlDom.parentNode.insertBefore(elem.parentNode.removeChild(elem), ctrlDom); }, toArray(target.childNodes)); iframe.parentNode.removeChild(iframe); removeAll(ctrlDom); myYield(_self); // もう一度最初から処理をやる }, function(iframe) { throw 'can not load next page: ' + url; }); })();
実際のコードは、もっとゴチャゴチャしてます。 先頭ページや最終ページに到達したときは処理を変えないと行けないし、 できれば「次ページへ」みたいなリンクは削除しておきたいわけです。 ちなみに、IE のどれかのバージョンだと、iframe の onload ハンドラがうまく呼ばれない ことがあるみたいです。そのときは onreadstatechange ハンドラを使えばよいらしい。 まぁ、今回は IE には対応できないと思うので、無視ですが・・・

« CSS ルールの動的な追加 | トップページ | 微妙な配列リテラル »