miau's blog?

PCサイトビューアで Google Suggest を動かしてみる

先週くらい?ふと思いついたので試してみました。
もっと大げさに書くなら「携帯で Ajax を動かしてみる」ということになるんだけど、まぁ制限が多いのでこういうタイトルに。




■発端

携帯(PCサイトビューア)で Ajax 使ってるようなサイトを見ても、結構見れないことが多い。
なぜって PCサイトビューアには XMLHttpRequest が無いから。

でも所詮ただのクラスなんだし、自分で作ってやればいいんじゃない?なんてことをふと思いまして。
SmartNav.js なんかは iframe で結構すごいことやってたし、自前の XMLHttpRequest でそれに近いことできるのでは?と。


■対象

でもよく考えると、iframe を使ってレスポンスを取得する場合、取得方法としては
・document.body.innerHTML
・document.body.innerText
の二種類くらいでは。

innerHTML のほうは DOM を元に構築される情報だし、innerText のほうもタグやらなにやら削られる(+ページの横幅で勝手に改行が入ったりする?)わけで。
どっちにしろ生のレスポンスを扱うことができないことに。
つまり、レスポンスとして XML が返ってくるようなサイトには対応できないことになる。

そんなわけで、テスト用の環境に選んだのは Google サジェスト
ここだったら、レスポンスは JavaScript のソースとして返ってくる(ページ内のスクリプトではそれを eval() してやる)形式だから、なんとかなりそう。


■完成品

紆余曲折は後回しにして、まずは完成品から。

emulate XMLHttpRequest

PCサイトビューアでの登録方法は、

(1) 上記リンクをクリック
(2) 画面に「bookmark this page!」と表示されるはずなので、そのページをお気に入りに追加
(3) お気に入りに「emulate XMLHttpRequest」というのがあるはずなので、その URL の先頭にある

'<title>emulate XMLHttpRequest</title><p>bookmark this page!'//

 の部分を削除

みたいな感じ。

使い方は、

(1) 表示モードを「PCスクリーン」にしておく
(2) Google サジェストを開く
(3) 上記 bookmarklet を動作させる。何か通信が発生するはず。
(4) テキストボックスに何か入力すると、PC のときと同じように候補がポップアップされるはず

こんな。ポップアップ後の候補選択周りが怪しいけど、細かいことは気にしない。
通信してる様子を確かめたい場合は、n.sa('style','height:0'); の部分をコメントアウト or 高さ変更するといいかも。


■動作の詳細

大雑把に分けると 3 つくらいの処理が動いてます。

(1) window.frameElement の定義
(2) XMLHttpRequest クラスの定義&初期化
(3) ページ内の script を全て再実行

ソースをちゃんとインデントして、簡単にコメントつけるとこんな感じ。

// PCサイトビューアには window.frameELement がないので自前で作成
var frameElement = null;

// XMLHttpRequest の定義
function XMLHttpRequest()
{
// XMLHttpRequest オブジェクトをカウントしておき、id のように使う
var nm = XMLHttpRequest._cnt++;

// オブジェクトの一覧を記憶しておく
XMLHttpRequest._o.push(this);

// プロパティの初期化。本当は status 等も準備すべきだが、長くなるので省略
this.readyState = 0;
this.onreadystatechange = null;

// open() メソッド。URL を記憶しておく。
this.open = function (m, u) {
this._u = u
};

// send() メソッド。iframe を生成し、タイマーを起動する。
this.send = function (a) {
// iframe の生成
var n = document.createElement('IFRAME');
n.sa = n.setAttribute; // 短縮名を定義
n.sa('src', this._u);
n.sa('style', 'height:0'); // display:none にはできない(後述)ので小細工
n.sa('frameborder', '0');
document.body.appendChild(n);

// タイマーの起動
this._timer = setInterval('XMLHttpRequest._o[' + nm + ']._tCall();', 1000);

// iframe をプロパティとして記憶しておく
this._if = n
};

// abort() メソッド。一応それっぽく実装。
this.abort = function () {
// 当初はここで document.removeChild(this._if) していたが、例外が発生するので削除
if (this._if) {
delete (this._if)
}
};

// 1 秒毎に呼ばれる関数。
this._tCall = function () {
// 読み込みが完了するまで document は取得できないので、document の有無で完了判定。
if (this._if.document) {
// タイマーの削除
clearInterval(this._timer);

// XMLHttpRequest に合わせて、プロパティに設定。
// 実際には &amp;、&lt;、&gt; 等も置換が必要。
this.responseText = this._if.document.body.innerText.replace(/&quot;/g, '"');
this.readyState = 4;

// onreadystatechange が設定されている場合、それを呼び出す。
if (this.onreadystatechange) {
try {
this.onreadystatechange.apply() // グローバルコンテキストで実行する
}
catch (e) {
alert(e)
}
}
}
}

}
// XMLHttpRequwst のクラス変数(のようなもの)を初期化
XMLHttpRequest._o = []; // XMLHttpRequest オブジェクトの一覧
XMLHttpRequest._cnt = 0; // XMLHttpRequest オブジェクトのカウント

// ページ内のスクリプトを全て再実行
(
function ()
{
var d = document, s = d.scripts, l = s.length, i, t; // 短縮名の定義
for(i = 0; i < l; i++) {
t = s[i].text;
try {
Function(t).apply() // グローバルコンテキストで実行する
}
catch (e) {
alert(e + t)
}
}

}
)()


■苦労したこと

というか、色々知らない制限や仕様があって困った。

・PCサイトビューアでは Window オブジェクトに frameElement が存在しない。
 ので、自分で用意するしかないっぽい。
・iframe のスタイルに display:none を指定した場合、その document を取得できないらしい。
 仕方ないので、style="height:0" と frameborder="0" の組み合わせを使用。
・例外が発生してもユーザに通知してくれない。
 いちいち try { 〜 } catch (e) { alert(e) } みたいに書いてたので面倒だった。
・以前書いたけど、お気に入りの URL には 1024 文字という制限があるみたい。
 長いプロパティを別の変数に置き換えたりとか、bookmarklet では当たり前のことだけど、これを携帯でやるのは面倒だった。


■ちなみに

Google サジェストは、XMLHttpRequest が無い場合には自前で iframe を用意して通信する仕様になってるみたい。(情報源はどこだったか忘れた)
実際京ぽん2の Opera には XMLHttpRequest が入ってないはずだけど、Google Suggest はちゃんと動作していた。

だから本当は window.frameElement やそれ以外の制限を回避してやればちゃんと動作させることは可能なはずなんだけど、今回やったように XMLHttpRequest のインターフェイスにあわせて iframe 使っていれば、わざわざ処理を分岐する必要ないので楽なんじゃないかなと。

そんなわけで、Ajax 系のサイトを作っている方は、
・クロスブラウザな XMLHttpRequest オブジェクト生成関数の最後に、上記のような自前クラス生成処理を準備
・responseText で取得しても問題ない形式でレスポンスを生成
していただけると、携帯で遊べるサイトが増えて嬉しい感じです。


■最後に

完成するまでは敢えて探さなかったんですが、似たようなことをやってる人もいるみたいです。


XMLHttpRequest風IFRAMEによるデータ通信1 - [JavaScript]All About

IFRAME を使った通信ということで、アプローチとしてはかなり近い。
ただ「XMLHttpRequest が使えない場合にこうすれば〜」という消極的な対策ではなく、新しいクラスを用意して「こっちを使ったほうがいいですよ」というスタンス。


Cross-Browser XMLHttpRequest - Web Site Design - Andrew Gregory's Web Pages

Java のランタイムを使った実装ということで、どうせ携帯では使えないんですが。
インターフェイスがかなりしっかり作られてるので、まじめに作りこむ場合に参考になりそうです。
posted at 03:39:39 on 2006-10-08 by miau - Category: W41CA No Trackbacks - Permalink

TrackBack

このエントリにトラックバックはありません
現在トラックバックは受け付けていません。

Comments

No comments yet

Add Comments

現在コメントは受け付けていません。
お手数ですが、 こちら のコメント欄にでも記載していただければと思います。