Ajax.Requestもどき隠しフレーム通信

2007/12/19追記。onExceptionオプションを追加しました。例外発生時の処理も変更しています。
2007/12/03追記。Opera9.24に対応しました。

以前書いたように、IE7ではローカル環境でAjaxが使えません。今まで使えたのに・・・。

拙作theater.jsはprototype.jsを使用しているとはいえ、Ajax関連は未使用です。けど、readmeでwikiのソースをそのまま使うためにAjax.Requestを使ってました。これがIE7だとまったく読めなくなります。

で、隠しフレーム通信に再登場願います。昔、Ajaxのなかった頃、ユーザーの注文に対して一日悩んだ挙句自力でこの方法を編み出すも、直後に持っていた本に載ってるのが発覚して凹んだことはすでになつかしい話・・・。

Ajax.Requestからの移行を容易にするために、インターフェースを似せてみます。

<html>
<head>
<title></title>
<script>
function getIFrameDocument(aID){
  if (document.getElementById(aID).contentDocument){  
    return document.getElementById(aID).contentDocument;
  } else {
    return document.frames[aID].document;
  }
}

var HFT = {};
HFT._index = 0;
HFT.Request = function(url, options) {
  this.url = url;
  this.options = options;
  var _this = this;
  var iframe = document.createElement('iframe');
  with(iframe.style){
    visibility = "hidden"; position = "absolute";
    top = "0px"; left = "0px"; width = "0px"; height = "0px";
  }
  //iframe.id = "_HFT_transport_" + new Date().getTime().toString();
  //連続実行時に同じ値になる場合もあるので連番に変更・・・。
  iframe.id = "_HFT_transport_" + ((HFT._index == Number.MAX_VALUE) ? 0 : HFT._index++);
  document.body.appendChild(iframe);
  try{
    iframe.src = url;
  }catch(e){
    dispatchException(e);
    return;
  }
  var count = 0;
  var tid = setInterval(function(){
    var fdb = null;
    try{
      fdb = getIFrameDocument(iframe.id).body;
      if(fdb == null) throw new Error();
    }catch(e){
      if(count++ > (options.timeout || 100)){
        clearInterval(tid);
        document.body.removeChild(iframe);
        dispatchException(e);
        return;
      }
      return;
    }
    clearInterval(tid);
    var responseText = (fdb.innerHTML.match(/\<pre\>((?:[^<]|\r|\n)*)(?:\<\/pre\>)?/i)||["",""])[1];
    responseText = responseText.replace(/&lt;/ig, "<")
                               .replace(/&gt;/ig, ">")
                               .replace(/&amp;/ig, "&");
    var transport = {
      responseText: responseText
    };
    options.onComplete(transport);
    document.body.removeChild(iframe);
  }, 100);
  
  function dispatchException(exception) {
    (options.onException || function(){})(_this, exception);
  }
};

function init(){
  new HFT.Request('test.txt', {
    onComplete: function(req){
      document.getElementById("test").value = req.responseText;
    },
    onException: function(hft, e){
      alert("HFT Error : " + hft.url);
    }
  });
}
</script>
</head>
<body onload="init();">
<textarea id="test" style="width:500px;height:500px;font-size:14px;"></textarea>
</body>
</html>
【test.txt】(utf-8)
<div>HFT.Requestのテスト。</div>
<div>
  <div>&&&</div>
</div>

読み込むファイルの拡張子はtxtにすること。htm等だと動作しません。

test.txtには改行、空白、タグがありますがそのまま取得できます。

init関数の中身に注目。

  new HFT.Request('test.txt', {
    onComplete: function(req){
      document.getElementById("test").value = req.responseText;
    },
    onException: function(hft, e){
      alert("HFT Error : " + hft.url);
    }
  });

「HFT」(Hidden Frame Transportの略)を「Ajax」に変えればprototype.jsAjax.Requestと同じ使い方ということがお分かりいただけると思います。・・・ただし、onComplete以外のoptionはないです。→12/19 オプションにonExceptionを追加しました。また、使えるのはresponseTextだけです。その他は追加未定^^;

(すみません、後で仕様整理します。。。)

あと、IE7が今手元にないのでIE7で動く保証が無いですwあとで確認して追記します・・・。→WinVista + IE7で動作を確認しました。動いてよかった^^;

※テキストファイルはutf-8にしましたが、shift_jisでも動作する模様。文字コードについての妥当な場合の条件はまだ不明確です。おいおい調べていきます・・・。

動作確認環境

OS WindowsXp, WindowsVista
ブラウザ IE6, IE7, Firefox2.0.0.11,Opera9.24

改行コード

上記で取得できるデータは、ブラウザによって改行コードが違います。

IE6,Opera9.24 \r\n
Firefox2.0.0.11 \n

扱う場合はご注意ください。