Textbox の Auto Complete (suggest っぽいの)
AutoComplete - MochiKit - Trac を見てもピンと来なかったので書いてみた。
汎用性無し、機能不足、負荷対策をしていないので、改良の余地がありますが、まー、雛形って事で。
autocomplete.html
<html> <head> <script type="text/javascript" src="/path/to/MochiKit.js"></script> <script type="text/javascript" src="/path/to/autocompleter.js"></script> <script type="text/javascript"> addLoadEvent( function () { new AutoCompleter("text", "select", ['dog', 'door', 'doctor', 'dock']); } ); </script> <style type="text/css" media="all"> #text { width: 10em; } #select { position: absolute; margin: 0; padding: 0; width: 10em; background-color: #FFFFFF; border: 1px solid #CCCCFF; } #select li { list-style-type: none; overflow: hidden; white-space: nowrap; font-size: 90%; } #select li.choice { color: #FFFFFF; background-color: #3366FF; } </style> </head> <body> <input id="text" type="text" name="text" maxlength="10" autocomplete="off"> <ul id="select"></ul> </body> </html>
ul と li タグを使って選択用のボックスを表示します。
選択中の要素(li タグ)には、choice というクラス名を付けているので、CSS による装飾が必要です。
autocompleter.js
var AutoCompleter = function (input_id, select_id, select_items) { bindMethods(this); this.input_elem = $(input_id); this.select_elem = $(select_id); this.select_items = $(select_items); this.prev_text = ''; this.hide_lock = false; this.highlight_elem = partial(this.set_class_name, 'choice'); this.clear_highlight_elem = partial(this.set_class_name, ''); connect(this.input_elem, 'onblur', this.blur); connect(this.input_elem, 'onkeyup', this.keyup); connect(this.input_elem, 'onkeydown', this.keydown); this.hide_select(); }; AutoCompleter.prototype = { blur: function (e) { this.hide_select(); }, keyup: function (e) { this.check(e.key().string); }, keydown: function (e) { this.choice(e.key().string); }, mouseover: function (index, e) { this.hide_lock = true; this.clear_highlight_elem(); this.choice_index = index; this.highlight_elem(); }, mouseout: function (index, e) { this.hide_lock = false; }, click: function (index, e) { this.choice_index = index; this.set_text(); this.hide_lock = false; this.hide_select(); }, hide_select: function () { if (this.hide_lock) { return; } this.show_items = null; this.show_item_elems = null; this.choice_index = null; hideElement(this.select_elem); }, show_select: function () { if ( isUndefinedOrNull(this.show_item_elems) || this.show_item_elems.length === 0 ) { return; } replaceChildNodes(this.select_elem, this.show_item_elems); showElement(this.select_elem); }, check: function (key) { var control_keys = ['ARROW_UP', 'ARROW_DOWN', 'ENTER', 'ESCAPE']; for (var i in control_keys) { if (key === 'KEY_' + control_keys[i]) { return; } } var text = this.get_text(); if (text === this.prev_text) { return; } this.prev_text = text; this.search(text); }, get_text: function () { return this.input_elem.value; }, set_text: function () { if ( isUndefinedOrNull(this.choice_index) || isUndefinedOrNull(this.show_items[this.choice_index]) ) { return; } this.input_elem.value = this.show_items[this.choice_index]; }, search: function (text) { this.hide_select(); if (isEmpty(text)) { return; } this.show_items = filter(partial(this.filter_item, text), this.select_items); this.show_item_elems = map(partial(LI, null), this.show_items); var methods = ['click', 'mouseover', 'mouseout']; for (var i in this.show_item_elems) { for (var j in methods) { connect( this.show_item_elems[i], 'on' + methods[j], partial(this[methods[j]], parseInt(i)) ); } } this.show_select(); }, filter_item: function (text, item) { var pos = item.toLowerCase().indexOf(text.toLowerCase()); if (pos === -1) { return false; } return true; }, choice: function (key) { switch (key) { case 'KEY_ARROW_UP': this.change_choice(-1); break; case 'KEY_ARROW_DOWN': this.change_choice(1); break; case 'KEY_ENTER': this.set_text(); this.hide_select(); break; case 'KEY_ESCAPE': this.hide_select(); break; } }, change_choice: function (increment) { if (isUndefinedOrNull(this.show_item_elems)) { return; } this.clear_highlight_elem(); this.set_choice_index(increment); this.highlight_elem(); }, set_choice_index: function (increment) { if (isNull(this.choice_index)) { this.choice_index = 0; return; } var index = this.choice_index + increment; if (index < 0 || this.show_item_elems.length <= index) { return; } this.choice_index = index; }, set_class_name: function (name) { if ( isUndefinedOrNull(this.choice_index) || isUndefinedOrNull(this.show_item_elems[this.choice_index]) ) { return; } setElementClass(this.show_item_elems[this.choice_index], name); } };