入力フォーム内のメニューを選択すると自動的にサーヴァから対応したメニューを読み込んでフォームを書換えると言うサンプルを解説します。
いわゆる俺の嫁を撰ぶフォームです。
三次元と二次元のどちらがお好きかを選択すると、それに応じてサーヴァから対応したメニューが読み込まれて後続のメニューが追加または変更されると言うものです。
先ず心掛けるべき事として、Ajaxはあくまでもユーザビリティを高めるための道具であり、ウェブブラウザにとって決して必須のものではないと言う事を忘れないでください。
つまり、Ajaxが使えない環境でもフォーム操作は可能にしなければなりません。
Ajaxに対応している環境は、DOMでのHTML操作が容易に出来る筈ですので、Ajaxに対応していない環境向けのフォームを用意し、Ajax対応環境ではそれをDOM操作で適切なフォームに変更するようにしましょう。
今回、そのような発想から、以下のHTML文書を用意しました。
Ajaxに対応していない場合、このフォームから送られたデータをもとに、新たな選択フォームを送信するようにします。
<form action="********" method="GET"><dl><dt>あなたは三次元派ですか? それとも二次元派?</dt><dd><select name="attr"><option value="" selected="selected">撰んでください</option><option value="3">三次元派</option><option value="2">二次元派</option></select></dd></dl><p><input type="submit" value="次へ"></p></form>
今回は、<select>要素は必ず<dl>要素内にあるようにマークアップします。
/ を付けておりませんが、XHTML文書の場合は必ず付けてください。データの形式にも依って処理コードが大きく変わりますが、今回はAjaxでは安全確実なXMLでデータを受け取る事とします。
今回受け取るデータの一例を以下に挙げておきます。
<?xml version="1.0" encoding="shift_jis"?><select name="nam"><option value="HORIKITA_Maki" selected="selected">堀北真希</option><option value="UETO_Aya">上戸彩</option>…</select>
ご覧のように、XMLと言っても、変更したい<select>要素のみを抜き出したXHTMLの切片に過ぎません。
以上の前提のもとで書いた、DynamicForm.jsのコードは以下のようになっております。
尚、このコードは HTTPRequest.js と併用する必要があります。
/*
Ajax(アジャックス)サンプルコード(3) - フォームの動的な変更。
※インターネットエクスプローラはウィンドウズ版の5.0以降を対象とします。
*/
/********************************************************************************
元のHTML文書に本処理を起動するための仕掛けをする処理。
********************************************************************************/
var pm_onload=window.onload;
window.onload=pulldownMenu_prepare;
function pulldownMenu_prepare() {
if (pm_onload) pm_onload();
var e=document.getElementsByTagName('select');
var i=e.length;
while (--i>=0) {
var c=' '+(e[i].getAttribute('name') || '')+' ';
if (c.indexOf(' attr ')<0) continue;
e[i].onchange=changeMenu;
}
}
/********************************************************************************
指定されたフォームを記述したXML文書をHTTP通信で取得するための処理。
********************************************************************************/
var h=null;
var pm_sel=null;
function changeMenu() {
// 選択に合わせて読み込むメニューを決める。
pm_sel=this;
var i=pm_sel.selectedIndex;
if (!i) return(false);
f=(i==1) ? 'DynamicForm3.xml' : 'DynamicForm2.xml';
// HTTP通信のセッティングを行う。
h=httpReq();
// HTTP通信をサポートしていないか、通信に失敗した場合、何もなかったように戻る。
if (!h) {
return(true);
}
// 通信状況が変わったとき(通信中⇒通信完了など)に実行すべき処理を指定する。
h.onreadystatechange=changeMenu2;
// 実際にHTTP通信でXML文書を取得する処理。
h.open('GET', f);
h.send(null);
return(false);
}
/********************************************************************************
通信状況が変化したときに呼び出される函数。
※実際には、HTTP通信が正常終了した場合のみ処理を行う。
********************************************************************************/
function changeMenu2() {
// 通信完了でなければ何もなかったように戻る。
if (h.readyState!=4) return(true);
// 正常に処理されなければ、エラーとして通信を途絶させて戻る。
if (h.status!=200) {
alert('HTTPエラー '+h.status+' が発生しました!');
h.abort();
return(false);
}
// XML文書を取り込む。
var txt=h.responseXML;
h.abort(); //この段階で通信を終了する。
proccessForm2display(txt); //フォーム追加処理に移る。
}
/********************************************************************************
取得したXML文書をフォームに追加して表示させる処理。
********************************************************************************/
function proccessForm2display(txt) {
/**********************************************************
取得したXML文書からのフォームの取得。
**********************************************************/
/*
※今回は<select>要素が丸ごと入ったものなので、
これをそのまま<dd class="_menu">要素の内容としてぶち込む。
*/
var t2=(txt.getElementsByTagName('select'))[0];
if (!t2) {
alert('データが壊れております!');
return(true);
}
/**********************************************************
追加するメニューを入れる場所を確保して、そこに追加。
**********************************************************/
// 当該<select>要素が入った<dl>要素のオブジェクトを得る。
var dl=pm_sel;
while ((dl=dl.parentNode)!=null) {
if (dl.tagName.toLowerCase()=='dl') break;
}
if (dl==null) {
alert('HTMLが壊れております!');
return(true);
}
// _menu クラスを持つ<dd>要素を探す。
var e=dl.getElementsByTagName('dd');
var i=e.length;
var j=null;
while (--i>=0) {
j=e[i].getAttribute('class') || e[i].getAttribute('className') || '';
if (j=='_menu') break;
}
// まだメニューが追加されていない場合に限り追加する。
var newMenu=null;
if (i<0) {
// 「<dt>それでは、あなたの"俺の嫁"はどなたですか?</dt>」を追加。
var e2=document.createElement('dt');
e2.appendChild(document.createTextNode('それでは、あなたの"俺の嫁"はどなたですか?'));
dl.appendChild(e2);
// 新しいメニューを入れる<dd>要素を追加。
newMenu=document.createElement('dd');
newMenu.setAttribute('class','_menu');
newMenu.setAttribute('className','_menu');
dl.appendChild(newMenu);
}
// 既にメニューが追加されている場合は中身を全部削除して空にする。
else {
newMenu=e[i];
while(newMenu.firstChild) newMenu.removeChild(newMenu.firstChild);
}
// メニューを追加する。
appendOthersNode(newMenu, t2);
return(false);
}
Ajaxを利用したフォームの動的変更処理のサンプルのコードの簡単な処理の流れは以下のようになります。
それでは、もう少し詳しく解説しましょう。
先ず、読み込み完了後にpulldownMenu_prepare() 函数を呼ぶように読み込み時に設定します。
pulldownMenu_prepare() 函数では、指定されたname属性値を持つ<select>要素(ここでは class="attr"属性を持った<select>要素)に対し、その選択肢が変更された際に changeMenu() 函数を割り込むようにDOM操作を行います。
changeMenu() 函数では、変更後の選択肢に対応するXML文書をHTTP通信で取得するようにしております。
また、通信完了時(実際には通信状況が変わった際)に通信で取得したXML文書の受け取り処理を担当するchangeMenu2() 函数を割り込むようにします。
これらの処理はAjaxでの文書類リクエスト処理内のデータを送信する必要がない場合のリクエスト方法でのコードと殆ど同じです。
ここで、注意すべき事として、thisオブジェクトを pm_sel 変数に保存しております。
これは、thisオブジェクトを下請の処理で利用するのですが、この値がAjax通信などで変更されてしまうからです。
changeMenu2() 函数はメニュー選択肢変更時に起動するAjax起動処理で、通信状況が変わった際に割り込む処理です。
実際には通信完了時以外には処理出来る事はないので、通信が完了した場合のみ処理を続けます。
正常にHTTPが実行された事を確認したうえで、取得したXHTML文書をXML文書のDOMとして受け取ります。
ここでの処理はAjaxでのデータ取得処理内のAjaxでXML文書としてデータを受け取る場合でのコードと殆ど同じですが、今回はDOMのエラーチェックは後で行う事としております。
また、DOMを受け取ったら、それをproccessForm2display() 函数に引渡します。
proccessForm2display() 函数はリクエストしたXML文書の受け取り処理であるchangeMenu2() 函数から呼び出されます。
引数として、取得したXML文書のDOMデータを受け取ります。
処理の内容は以下の通りです。
XML文書では<select>要素がルート要素となりますが、ここでは普通に零番目の要素を抽出する形としております。
勿論、抽出出来なければエラーとします。
具体的には、親要素を一段階ずつ辿って<dl>要素が見付かるまで繰り返すと言うものです。
_menu クラス名を持つ<dd>要素があるかどうかを調べます。
具体的には当該<dl>要素内の<dd>要素内に該当するものがあるかを調べます。
_menu クラス名を与えた<dd>要素ノードを作成して追加します。
このとき、その前に それでは、あなたの"俺の嫁"はどなたですか? と言うメッセージを入れた<dt>要素を作成して追加します。
尚、インターネットエクスプローラでは、DOMでクラス名を扱う場合には class属性ではなく className属性を扱う事とされているため、class 属性と className 属性の双方を設定します。
_menu クラス名を与えられた<dd>要素に追加します。
但し、このXMLはフォームのあるHTML文書と異なる文書のものですので単純に移転させる事は出来ません。
このため、HTTPRequest.js 内にある appendOthersNode() 函数を用いて移転させる事となります。
今回のコードをご覧になって、<select>要素や<dl>要素にidを付ければ、簡単に要素の操作が出来るではないかと思われた方もいるかも知れません。
しかし、id属性で要素を特定しようとすると、一つのページに複数のフォームがあってもそのうちの一つのみしか対象に出来なくなります。
このため、今回は操作された<select>要素のオブジェクトを与えるthisオブジェクトを元に、その上位要素を調べるなどして対応する<dl>要素を特定しております。
さて、今回のコードの解説をご覧になって、わざわざこんな廻りくどいAjaxなど導入せず、はじめからHTML文書にフォームの選択肢を記述しておけば良いではないかと言う方もいらっしゃるかも知れません。
ですが、例えば郵便番号の検索や個人情報の提出のため住所を入力すると言うような場合はどうでしょう。
住所入力の場合、都道府県名, 区市町村名, そして町名と言う段取りで入力して行く訳ですが、初めから全国の全市町村名と町名のリストをHTMLの中に記述出来るものでしょうか。
このように、全ての範囲を網羅したフォームを用意する事が事実上困難な場合には、非常に有効な方法と言えるでしょう。
もっとも、それだけ膨大なデータを扱うとなると、わざわざ個人でデータを用意するのは困難かと思われます。
現在、大手のサーヴィス業者がその手の膨大なデータを扱えるようなAPIを公開しているので、それを利用する事になるでしょう。
Copyright ©平成21年-平成24年 さいたま・しらぎくさいと 版権所有
marguerite.site@gmail.com