jQuery全盛の今こそYUI
猫も杓子もjQuery。いささかうんざりしてる。
まあYUI3になってYUI2のコンポーネントを捨てる気なのかまだ作る気があるのかギャラリーだけで対応しろってことなのかよくわからんのとCDN半強制はちょっといただけないが、現在のところYUI2で欲しいコンポーネントはそろってる。
jQueryのプラグインだと作る人によってインターフェースがまちまちで一貫性がなくなるとかバージョン追うのが大変とか色々あるわけで。だから規模が大きくなってくる、あるいは作る人が増える場合はYUIの方が合わせやすい。
特にイベント処理ではjQueryがいちいちクロージャー作らないとコンテキストがDOM要素そのものになるのがYUIだと自分でコンテキストオブジェクトを指定できる所なんか、自前のjavascriptクラス作る場合には強力。というか俺自身いつも「thisは誰?」って考てしまうからそれが嫌なの。
あと継承とカスタムイベント。jQueryにも継承はあるけど、カスタムイベントを使う場合イベント発火オブジェクトがjQueryにならなきゃいけないなんてちょっと大げさすぎる気がする。event=new CustomEvent();event.fire()の方が好き。
そんな状況下で「階層選択式ドロップダウン」を作った。必要に迫られて作ったのだがそこそこ使えそうなので公開する。
<div id="container"></div> <input type="hidden" id="selectedValueJson" /> <script> if(typeof(HieroSelect) == "undefined"){ var yjson = YAHOO.lang.JSON; var yevent = YAHOO.util.Event; HieroSelect = function(id,item){ this.item = item; this.id = id; this.container = document.getElementById(id); this.selectedValueJson = document.getElementById("selectedValueJson" ); this.init(); } HieroSelect.prototype={ init : function(){ this.selectedValues = yjson.parse( this.selectedValueJson.value ); var position = 0; this.clearContainer(); this.createSelect(this.item,position); } ,clearContainer : function(){ var children = this.container.childNodes; var len = children.length; for(var i = len -1;i>=0 ;i--){ this.container.removeChild(children[i]); } } ,createSelect : function(item,position){ if(item.subItems.length == 0) return; var selectedValue = (position < this.selectedValues.length) ? this.selectedValues[position] : ""; var select = document.createElement("select"); var itemLength = item.subItems.length; var option = this.createOption("","(選択してください)"); select.appendChild(option); var selectedItem = null; for(var i = 0;i <itemLength;i++){ var subItem = item.subItems[i]; var option = this.createOption(subItem.text,subItem.text); if(subItem.text == selectedValue){ option.setAttribute("selected","selected"); selectedItem = subItem; } select.appendChild(option); } yevent.on(select,"change",function(event){ this.onchange(event,item,position); },this,true); this.container.appendChild( select ); if(selectedItem != null){ this.createSelect(selectedItem,position + 1); } } ,createOption : function(value,text){ var option = document.createElement("option"); option.setAttribute("value",value); var text = document.createTextNode(text); option.appendChild(text); return option; } ,onchange : function(event,item,position){ var select = yevent.getTarget(event); var value = select.options[select.selectedIndex].text; if(position < this.selectedValues.length){ this.selectedValues.splice(position); } this.selectedValues[position] = value; if(value!= ""){ var children = this.container.childNodes; var len = children.length; for(var i = len -1;i>position;i--){ this.container.removeChild(children[i]); } var selectedItem = this.find(item,value); if(selectedItem != null){ this.createSelect(selectedItem,position + 1); } } else{ } this.selectedValueJson.value = yjson.stringify(this.selectedValues); } ,find : function(item,value){ var itemLength = item.subItems.length; var selectedItem = null; for(var i = 0;i <itemLength;i++){ var subItem = item.subItems[i]; if(subItem.text == value){ selectedItem = subItem; } } return selectedItem; } } } var data = { text:"", subItems : [ { text:"選択1", subItems: [ { text : "選択1−1", subItems : [] }, { text : "選択1−2", subItems : [] }, { text : "選択1−3", subItems : [] } ] }, { text:"選択2", subItems: [ { text : "選択2−1", subItems : [] }, { text : "選択2−2", subItems : [] }, { text : "選択2−3", subItems : [] } ] } ] } var hiero = new HieroSelect("container",data); </script>
こんな感じで。dataには { text:"hoge",subItems[] }の形を持つツリーデータを渡す。ツリーのルートが無いと(dataが配列だと)上手くいかない。
最終的には選択したテキストがhiddenフィールドにJSON配列として格納されるので、あとはサーバーサイドで適宜JSON展開して使えばいい。
YAHOO.util.Event,YAHOO.lang.JSONを使っているのでutilities.js(かdom.jsとevent.js)とjson.jsを参照するのを忘れずに。