optionを動的に変える

この間インクリメンタルサーチを試したけど、実務で使う機会はまずないでしょう(自分の場合)。じゃAjaxで何するのよ?って考えて思い浮かんだのが「コンボボックスやリストボックスの選択肢(option)を動的に変える」というもの。たとえば

  • 大分類、中分類、小分類を選択するコンボボックスがある。
  • 大分類の項目を選択すると、それに関連する項目が中分類のコンボボックスにセットされる。同時に小分類の項目はクリアされる。
  • 中分類を選択すると、小分類のコンボボックスに関連項目がセットされる。

といった感じね。今まではコンボボックスの隣に[選択]みたいなボタンを作っておいて、それが押されたタイミングで下位のコンボボックスを操作していたわけよ。でもわずらわしいのね。というわけで作ってみた(別にAjaxを使わなくてもできるけど...(汗)。

クライアント側はこんな感じ。

<html lang="ja">
<head>
<title>Comb test</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<script type="text/javascript">
var xmlhttp;   // XMLHttpRequest用の変数
var target;

function startup() {
	// XMLHttpRequestを生成
	xmlhttp = this.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
}

function selectChanged(sel, sel2){
	var val = sel.options[sel.selectedIndex].value;
	target = sel2;

	// 前回の通信をキャンセル
	xmlhttp.abort();
	// サーバから返答があったときの処理を設定
	xmlhttp.onreadystatechange = handleXmlhttp;
	// GETで送信
	xmlhttp.open("GET", "http://hogehoge/selectchanged?val=" + encodeURI(val), true);
	// GETの場合はnull
	xmlhttp.send(null);
}

// xmlhttpの状態が変化したら起動される	
function handleXmlhttp(){
	if (xmlhttp.readyState == 4) {
		if (xmlhttp.status == 200) { // 正常にデータの取得が完了
			var opt = eval(xmlhttp.responseText); // JSON形式を復元
			if(opt != null) {
				target.options.length = opt.length;
				for(var i=0; i<opt.length; ++i) {
					target.options[i].value = opt[i][0];
					target.options[i].text = opt[i][1];
				}
				target.options[0].selected = true
		 	}
		}
	}
}
</script>
</head>
<body onload="startup();">
<form action="..." method="..." name="form1">
大分類:<select name="level1" onChange="selectChanged(this, level2);level3.options.length=0;">
	<option value=""></option>
	<option value="A">TYPE-A</option>
	<option value="B">TYPE-B</option>
	<option value="C">TYPE-C</option>
</select>
<P>
中分類:<select name="level2" onChange="selectChanged(this, level3)">
	<option value=""></option>
</select>
<P>
小分類:<select name="level3">
	<option value=""></option>
</select>
<P>
            :
</form>
</body>
</html>

selectChanged()は大分類/中分類で共用できるようにしてみた。先頭の空項目を選択されたとき、ちょっと見栄えが?になることもあるけど、この方法ならコンボボックスがいくつになっても対応できると思う(理屈の上では。試してないけど)。

一方、サーバ側のselectchangedはあまりというか、全然見るところ無しなんだけど一応載せてみる。

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONArray;

public class SelectChanged extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text/xml; charset=UTF-8");
		PrintWriter out = response.getWriter();

		String val = request.getParameter("val");

		String[] abc = {"A", "B", "C"};
		// optionを格納するJSONオブジェクト
		JSONArray result = new JSONArray();
		
		// 先頭に空のデータを追加
		JSONArray empty = new JSONArray();
		empty.put("");
		empty.put("");
		result.put(empty);
		
		// ""以外が選択されていればoptionデータを追加
		if (!val.equals("")){
			for (int i=0; i<abc.length; i++){
				JSONArray array = new JSONArray();
				array.put(val + abc[i]);
				array.put("TYPE-" + val + abc[i]);
				result.put(array);
			}
		}
		// 生成結果を返す
		out.println(result.toString());
	}
}

なんと空文字と選択された項目に"A", "B", "C"を付加したものを返すだけ(苦笑。実務ならDBを読みに行くところだけどね。あと渡された文字列が何を表すか(大分類なの?中分類なの?)を知らせるデータなんかも必要になると思うけど今回はパス。

一応IE6.0とFireFox1.0では動いた。