AJAX プログラミング入門 - US Zip コードの解決

Ajax と XML Web Service を組み合わせて、アメリカの郵便番号 (US Zip コード) の解決を行う例を示します。 日本にお住まいの方にはあまり馴染みがないかもしれませんが、アメリカの郵便番号は5桁の数字で表され、 例えば私の住むロサンゼルス郡 Torrance 市は 90501 になります。ここでは、郵便番号 (例えば 90501) から Torrance という情報を取得します。このとき Ajax を用いて動的に行う例を使って Ajax について説明します。

Ajax 概要

Ajax は Asynchronous JavaScript and XML の略です。Ajax を構成するものがすべて全く新しいテクノロジというものではありませんが、JavaScript や XML を含む各種オブジェクトをうまく組み合わせることによって、見た目には大変美しい動作をするスクリプトを作ることが可能になります。

上記の例では、テキストボックスに情報が入力されたら直ちに (ページ全体をリフレッシュすることなく)、 サーバーに対して入力された郵便番号に対応する市町村の情報を取りに行きます。サーバーから応答があり次第、 テキストボックスの下に埋め込まれた span タグの中に、市町村の情報を書き入れます。


fig1. 概要

プログラミング方法

はじめにお断りしておきますと、私は prototype.js を使っています。この資料でのコードには prototype.js 特有のコードの書き方が多々出てきますが、それはその都度ご説明します。prototype.js は http://www.prototypejs.org/ からダウンロードできるスクリプトライブラリ (フレームワーク) で、元の JavaScript を上手に拡張してくれ、かつ、シームレスにマルチブラウザに対応するのに非常に役立ちます。 Ajax を意識するしないにかかわらず、JavaScript を使ってコードを書く人は皆、prototype.js を使って損はないと思います。

さっそく今回の例のコードをお見せします。こちらです。

Event.observe(window, 'load', window_onload, false);

function window_onload(evt) {
  Event.observe('txt', 'keyup', txt_onkeyup, false);
}

function txt_onkeyup(evt) {
  if (!$('txt').value.match(/\b[0-9]{5}$/)) {
    $('zip_result').innerHTML = '5 桁の数字を入力してください';
    return;
  }

  update_zip();
}

function update_zip() {
  new Ajax.Request('get_addr_info.php', {
    method: 'POST',
    parameters: {
      zip: $('txt').value
    },
    onComplete: function(trans) {
      set_zip(trans.responseText);
    }
  });
}

function set_zip(t) {
  var zip = eval('(' + t + ')');

  if (zip.length > 0) {
    var sb = new TKStringBuffer();

    for (var i = 0; i < zip.length; i++) {
      sb.a(zip[i].city);
      sb.a(', ');
      sb.a(zip[i].state);

      if (i < zip.length - 1) {
        sb.a('<br>');
      }
    }

    $('zip_result').innerHTML = sb.toString();
  } else {
    $('zip_result').innerHTML = '存在しない ZIP コードです';
  }
}

上記をひとつずつご説明します。

Event.observe(window, 'load', window_onload, false);

この呼び出しによって、window オブジェクトの load イベントのイベントハンドラをセットします。 本サイト内 「イベント処理の基本」でも説明しているように、イベントハンドラの設定は (残念ながら) ブラウザによって手順が異なります。その違いを吸収するために、 prototype.js の Event.observe メソッドを利用してイベントハンドラを設定しています。 load イベントはドキュメント内のすべてのコンテンツがロードされた直後に呼び出されます (The load event occurs when the DOM implementation finishes loading all content within a document, all frames within a FRAMESET, or an OBJECT element. W3C DOM Event Level 2 より)。

function window_onload(evt) {
  Event.observe('txt', 'keyup', txt_onkeyup, false);
}

さて、window オブジェクトの load イベントハンドラである、window_onload ファンクションは上記のとおりです。 ここではさらに、テキストボックスの keyup イベントハンドラを設定しています。 このように、Event.observe 関数の第一引数には、オブジェクトの参照そのもの (例: window) の他、 HTML (DOM) 要素の id を渡すことも可能です。

function txt_onkeyup(evt) {
  if (!$('txt').value.match(/\b[0-9]{5}$/)) {
    $('zip_result').innerHTML = '5 桁の数字を入力してください';
    return;
  }

  update_zip();
}

keyup イベントハンドラは上記です。入力された文字列が 5 桁の数字ではない場合、 5 桁の数字を入力してください というメッセージを表示し、5 以上の場合は update_zip 関数を呼び出します。

「5 桁の数字かどうか」調べるために、ここでは正規表現を使っています。 /\b[0-9]{5}$/ の部分です。/ と / で区切ってパターンを記載します。 \b は単語の区切りの始め、$ は区切りの終わりです。[0-9] は「数字の 0 から 9」。 {} で、長さ 5 を記載しています。 つまり、
/ (正規表現のパターンの始まり...)
\b (単語の区切りで...)
[0-9] ( 0 から 9 までの文字が... )
{5} (ちょうど 5 個あって...)
$ (単語が終わる)
/ (正規表現のパターンの終わり)
というものに、value がマッチするかどうか、上記の方法でチェックすることができます。

少し Ajax から脱線しました。話を元に戻します。

さて、入力された文字が 「5桁の数字」であった場合、update_zip が呼び出されます。 update_zip は次のとおりです。

function update_zip() {
  new Ajax.Request('get_addr_info.php', {
    method: 'POST',
    parameters: {
      zip: $('txt').value
    },
    onComplete: function(trans) {
      set_zip(trans.responseText);
    }
  });
}

ここがサーバーに問い合わせしている箇所です。'get_addr_info.php' という URL に対して、メソッド 'POST' で、 パラメータは zip という名前で、値が $('txt').value です。これはすなわちテキストボックスに入力された値です。 onComplete には、サーバーからの応答を受け取る関数を設定します。この関数の第一パラメータには、 サーバーから返された response オブジェクトが格納され、.responseText でサーバーから返された文字列そのものが格納されています。 したがって、以下の箇所ではサーバーから返された文字列をそのまま、set_zip 関数に渡していることになります。

    onComplete: function(trans){
        set_zip(trans.responseText);
    } 

set_zip 関数は次です。

function set_zip(t) {
  var zip = eval('(' + t + ')');

  if (zip.length > 0) {
    var sb = new TKStringBuffer();

    for (var i = 0; i < zip.length; i++) {
      sb.a(zip[i].city);
      sb.a(', ');
      sb.a(zip[i].state);

      if (i < zip.length - 1) {
        sb.a('<br>');
      }
    }

    $('zip_result').innerHTML = sb.toString();
  } else {
    $('zip_result').innerHTML = '存在しない ZIP コードです';
  }
}

前述のとおり t にはサーバーから返された文字列そのものが格納されています。 今回、サーバーからは JSON (JavaScript Object Notation) 形式で応答を返しています。eval( JSON 文字列 ) で、 JSON 文字列を評価すると、サーバーで作られたオブジェクトがクライアントに配列として再構築されます。 従って結果に要素がひとつ以上ある場合は、それを innerHTML に格納します。

尚、ここで TKStringBuffer なるオブジェクトを作成していますが、以下のようなものです。 これは prototype.js の一部ではなく、わたしが書いたものです。 文字列の結合操作は非常に時間のかかる作業なので、それを極力避けています。 良かったら使ってください。要は配列に結合したい文字列をどんどん貯め、 toString() メソッドでその内部配列内の文字列を一度に結合しています。 これについては当サイトのJavaScript の文字列の結合を最適化する で説明しています。

/* StringBuilder */

function TKStringBuffer() {
  this.__buffer = new Array();
}

TKStringBuffer.prototype = {
  clear: function() {
    this.__buffer = new Array();
  },
  append: function(s) {
    this.__buffer.push(s);
  },
  appendn: function(s) {
    this.append(s);
    this.append('\n');
  },
  toString: function() {
    return this.__buffer.join('');
  },
  /* Alias */
  a: function(s) {
    this.append(s);
  },
  an: function(s) {
    this.appendn(s);
  }
};

以上で、入力文字列を用いてサーバーから情報を動的に取得して span 要素に文字をセットすることができました。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説