option要素を絞り込むjquery pluginを作ってみるメモ

rhacoの中の人の
このセリフ
をきっかけに、久しぶりにjqueryを触ってみました(最近はExtばっかりやってたので)。ついでに折角なのでplugin化してみました。以下は作成手順、ポイントなどのメモです。現在のrhacoのtrunkがjquery-1.2.1を使っていたので、1.2.1で作成しました。

実際に動いているのはrhacoのtrunk(rev 2762)以降のsetup.phpのrhacodocuなどで確認することが出来ます。

pluginの機能
・テキストフィールドへの入力値を用いてselectタグのoption要素を絞り込む
・フィールドが空になったらoption要素を元に戻す
・絞込みを行う最低文字数を設定できるようにする。

作成手順は
1.とりあえず機能を作る
2.まとめる
3.plugin化する
という流れにしました。
要は「とりあえずコードを書いてリファクタリング」です。

ソース
使用したHTML。最初はid指定の方が楽だったので、idを指定しています。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="jquery-1.2.1.pack.js"></script>
<script type="text/javascript" src="filterOptions.js"></script>
<title>filtering test</title>
</head>
<body>
<form>
<input type="text" id="hoge" name="hoge" value="test" class="h" />
<select id="fuga" name="fuga" class="h">
<option value="111111">あああああ</option>
<option value="222222">あいいいい</option>
<option value="333333">あううううう</option>
<option value="444444">あいううう</option>
<option value="555555">あうえええ</option>
</select>
</form>
</body>
</html>

以下のソースは全てfilterOptions.jsで用意します。

1.とりあえず機能を再現する
とりあえず機能を再現できるかを試したかったので、「動けば良い」つくりです。楽するためにid指定にしています。onReady相当の場所に色々入れていたり、作りも何か変です。でも動きますw。まだ文字数指定は出来ません。

$(function(){var si = new searcher(jQuery('#hogyo'));
jQuery('#hoge').bind('keyup',function(){
var query = $(this).val();
if(query){
	jQuery('#'+si.id+' > not:(:contains('+query+'))').remove();
}else{
	jQuery('#'+si.id).replaceWith(si.clone.clone(true));
}})
});

var searcher = function(el){
	this.clone = el.clone(true);
	this.id = el.attr("id");
}

2.まとめる
設定を保存している箇所と絞り込む箇所が分かれてたりと構成が変なので、まとめます。

var refineOptions = function(select){
	var stock = {clone:select.clone(true),id:select.attr("id")};
	return function(el){
		var query = $(el).val();
		if(query){
			jQuery('#'+stock.id+' > :not(:contains('+query+'))').remove();
		}else{
			jQuery('#'+stock.id).replaceWith(stock.clone.clone(true));
		}
	}
}
$(function(){var rf = new refineOptions(jQuery('#hogyo'));
jQuery('#hoge').bind('keyup',function(){rf(this)})});

onReady部に書くコードの量が減って、機能と設定をまとめることが出来ました。

3.plugin化
機能をまとめることが出来たので、pluginにあてはめてみました。jQueryのplugin作成手順のページを参考にしました。

jQueryはチェインでどんどんつなげていくスタイルなので、渡される要素は複数あるケースが多いので、上の機能を複数の要素にあてはめる必要があります。idが存在しないケースもあるので、idが無い場合は作る必要があります。また上のケースではnewすることで関数をreturnさせ、clickイベントにbindさせていましたが、これも中に組み込んでしまいます。要は

・複数要素への対応
・plugin内で完結する

点に注意して、リファクタリングしました。

jQuery.fn.filterOptions = function(chars){
	var stock = [];
	this.each(function(i){
		if($(this).is("select")){
			if(!$(this).attr("id")) $(this).attr("id","_s"+i);
			stock.push({clone:$(this).clone(true),id:$(this).attr("id")});
		}
	});
	this.each(function(i){
		if($(this).is("input") && $(this).attr("type")=="text"){
			if(!$(this).attr("id")) $(this).attr("id","_i"+i);
			$(this).bind('keyup',function(){filter(this)});
		}
	});
	var filter =  function(el){
		var query = $(el).val();
		if(query && (query.length >= chars) ){
			$.each(stock,function(i){jQuery('#'+stock[i].id+' > :not(:contains('+query+'))').remove()});
		}else{
			$.each(stock,function(i){jQuery('#'+stock[i].id).replaceWith(stock[i].clone.clone(true))});
		}
	}
}
$(function(){jQuery('.h').filterOptions(4)});//4文字以上

と、こういう手順で作成することで、スムーズにpluginを作成できました。
プラグインにするぞー!」という目標があったおかげでリファクタリングも楽しくできたような気がします。Extは見た目の楽しさはあるけど、こういう楽しみは少ないなー。

結論:plugin作成は楽しい。jqueryも楽しい。