堀北真希うさぎ
主な作品
堀北真希うさぎ:平成元年〜平成 3年頃のボシュロム製品『オプティマ』のコマーシャル風にコンタクトレンズを入れている場面を含んだ仮想CM動画

ページ案内

Ajaxを利用した動的なフォーム変更処理。

入力フォーム内のメニューを選択すると自動的にサーヴァから対応したメニューを読み込んでフォームを書換えると言うサンプルを解説します。

Ajaxを利用した動的なフォーム変更処理・目次。

Ajaxを利用した動的なフォーム変更のサンプル。

いわゆる俺の嫁を撰ぶフォームです。

三次元と二次元のどちらがお好きかを選択すると、それに応じてサーヴァから対応したメニューが読み込まれて後続のメニューが追加または変更されると言うものです。

あなたは三次元派ですか? それとも二次元派?

Ajaxを利用した動的なフォーム変更の実際でのマークアップとデータとコード。

HTML文書側のマークアップ。

先ず心掛けるべき事として、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>要素内にあるようにマークアップします。

実際に受け取るデータ。

データの形式にも依って処理コードが大きく変わりますが、今回は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の切片に過ぎません。

Ajaxを利用して動的にフォームを変更する実際のコード。

以上の前提のもとで書いた、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を利用したフォームの動的変更処理のサンプルコードの説明。

Ajaxを利用したフォームの動的変更処理のサンプルコードの具体的な処理の流れ。

Ajaxを利用したフォームの動的変更処理のサンプルのコードの簡単な処理の流れは以下のようになります。

  1. 先ず、HTML文書読み込み完了時に、ターゲットとなるname属性値を持つ<select>要素に対し、選択肢が変更された場合にAjax起動処理を起動するようにします。
  2. Ajax起動処理では、操作された<select>要素の現在の状況に合わせて、対応するデータとなるXML文書をリクエストするようにします。尚、撰んでくださいが選択された場合には、受け取るべきデータは無いのでそのまま戻ります。また、Ajaxに対応していない場合は、処理をそのまま続けられるようにします。
  3. リクエストされたXML文書は受け取り処理でDOM形式で受け取り、それを表示させる処理に引き渡します。
  4. 取得したXML文書をフォームに追加して表示させる処理では、操作された<select>要素の上位要素である<dl>要素内にもう一つのフォームを収めるための<dt>要素と<dd>要素がまだ作成されていなければ作成して、そこにメッセージと新しいフォームを入れます。

それでは、もう少し詳しく解説しましょう。

HTML文書読み込み完了時の処理。

先ず、読み込み完了後にpulldownMenu_prepare() 函数を呼ぶように読み込み時に設定します。

pulldownMenu_prepare() 函数では、指定されたname属性値を持つ<select>要素(ここでは class="attr"属性を持った<select>要素)に対し、その選択肢が変更された際に changeMenu() 函数を割り込むようにDOM操作を行います。

メニュー選択肢変更時に起動するAjax起動処理。

changeMenu() 函数では、変更後の選択肢に対応するXML文書をHTTP通信で取得するようにしております。

また、通信完了時(実際には通信状況が変わった際)に通信で取得したXML文書の受け取り処理を担当するchangeMenu2() 函数を割り込むようにします。

これらの処理はAjaxでの文書類リクエスト処理内のデータを送信する必要がない場合のリクエスト方法でのコードと殆ど同じです。

ここで、注意すべき事として、thisオブジェクトを pm_sel 変数に保存しております。

これは、thisオブジェクトを下請の処理で利用するのですが、この値がAjax通信などで変更されてしまうからです。

リクエストしたXML文書の受け取り処理。

changeMenu2() 函数メニュー選択肢変更時に起動するAjax起動処理で、通信状況が変わった際に割り込む処理です。

実際には通信完了時以外には処理出来る事はないので、通信が完了した場合のみ処理を続けます。

正常にHTTPが実行された事を確認したうえで、取得したXHTML文書をXML文書のDOMとして受け取ります。

ここでの処理はAjaxでのデータ取得処理内のAjaxでXML文書としてデータを受け取る場合でのコードと殆ど同じですが、今回はDOMのエラーチェックは後で行う事としております。

また、DOMを受け取ったら、それをproccessForm2display() 函数に引渡します。

取得したXML文書をフォームに追加して表示させる処理。

proccessForm2display() 函数リクエストしたXML文書の受け取り処理であるchangeMenu2() 函数から呼び出されます。

引数として、取得したXML文書のDOMデータを受け取ります。

処理の内容は以下の通りです。

  1. 先ず、取得したXML文書から<select>要素を取り出します。

    XML文書では<select>要素がルート要素となりますが、ここでは普通に零番目の要素を抽出する形としております。

    勿論、抽出出来なければエラーとします。

  2. 続いて、操作した<select>要素の上位にある<dl>要素を探し、見付からなければHTMLの異常としてアラートを出して終了します。

    具体的には、親要素を一段階ずつ辿って<dl>要素が見付かるまで繰り返すと言うものです。

  3. 更に、当該<dl>要素内に _menu クラス名を持つ<dd>要素があるかどうかを調べます。

    具体的には当該<dl>要素内の<dd>要素内に該当するものがあるかを調べます。

  4. 以上の準備を経て、抽出したXMLを _menu クラス名を与えられた<dd>要素に追加します。

    但し、このXMLはフォームのあるHTML文書と異なる文書のものですので単純に移転させる事は出来ません。

    このため、HTTPRequest.js 内にある appendOthersNode() 函数を用いて移転させる事となります。

補足。

今回のコードをご覧になって、<select>要素や<dl>要素にidを付ければ、簡単に要素の操作が出来るではないかと思われた方もいるかも知れません。

しかし、id属性で要素を特定しようとすると、一つのページに複数のフォームがあってもそのうちの一つのみしか対象に出来なくなります。

このため、今回は操作された<select>要素のオブジェクトを与えるthisオブジェクトを元に、その上位要素を調べるなどして対応する<dl>要素を特定しております。

Ajaxを用いた動的なフォーム変更機能の用途。

さて、今回のコードの解説をご覧になって、わざわざこんな廻りくどいAjaxなど導入せず、はじめからHTML文書にフォームの選択肢を記述しておけば良いではないかと言う方もいらっしゃるかも知れません。

ですが、例えば郵便番号の検索や個人情報の提出のため住所を入力すると言うような場合はどうでしょう。

住所入力の場合、都道府県名, 区市町村名, そして町名と言う段取りで入力して行く訳ですが、初めから全国の全市町村名と町名のリストをHTMLの中に記述出来るものでしょうか。

このように、全ての範囲を網羅したフォームを用意する事が事実上困難な場合には、非常に有効な方法と言えるでしょう。

もっとも、それだけ膨大なデータを扱うとなると、わざわざ個人でデータを用意するのは困難かと思われます。

現在、大手のサーヴィス業者がその手の膨大なデータを扱えるようなAPIを公開しているので、それを利用する事になるでしょう。

しらぎくのウェブサイト作成入門サイトマップ



marguerite.site@gmail.com