I hacked together a simple MochiKit-based ComboBox and am posting it in
case it could be useful to anyone. If there's a better way to attach
source here than cut and paste, please let me know.
Thanks,
Eric
(pasted into this buffer below: my_combobox.js, my_combobox.css,
my_combobox_sample.html, visibility.js, and visibility.css)
(If you want a graphic button, this looks for a "combo_box_arrow.png"
(not included))
my_combobox.js
----------------------------
/*
Simple MochiKit-based ComboBox
Eric Waldheim
Cobbled together from bits and pieces of:
ComboBox By Jared Nuzzolillo
ComboBox Updated by Erik Arvidsson
DHTML Widgets by Barney Boisvert
Dojo ComboBox
*/
if (typeof(My) == 'undefined')
{
My = {};
}
My.ComboBox = function(id, options)
{
this.__init__(id, options);
};
My.ComboBox.prototype =
{
__class__: My.ComboBox,
__init__: function(id, /* optional */config)
{
this.node = $(id);
this.exposed_options = [];
this.config = MochiKit.Base.update(
{
maxListLength:10,
options:[],
optionStringGetter:itemgetter(0)
},
config || {});
this.textedit = INPUT({type:'text', 'class':"cbox-input"});
var button = BUTTON({class:'cbox-button'},
IMG({src:'combo_box_arrow.png',
alt:'show options'}));
this.optionslist = DIV({'class':'cbox-list invisible'});
appendChildNodes(this.node,
this.textedit,
button, this.optionslist);
connect(button, "onfocus", function () { button.blur(); });
connect(button, 'onclick', bind(this.toggle, this));
connect(this.optionslist, "onselectstart", function(){return
false});
connect(this.optionslist, "onclick", bind(this.clickOption,
this));
connect(this.node, "onkeydown", bind(this.handleKey, this));
connect(this.node, "onkeyup", bind(this.handleKeyUp, this));
connect(this.node, "onmousedown", bind(this.mouseDown, this));
},
mouseDown: function(event)
{
var el = event.target();
while (el.nodeType != 1) el = el.parentNode;
var elcl = el.className;
if (elcl.indexOf("cbox-")!=0)
{
makeInvisible(this.optionslist);
}
else
{
this.textedit.focus();
this.moveCaretToEnd();
}
},
_prevTexteditValue: "",
handleKey: function(event)
{
_prevTexteditValue = this.textedit.value;
var key = event.key();
switch(key.code) {
case 38: // up arrow
this.highlightPrevOption();
event.stopPropagation();
break;
case 40: // down arrow
if (!isVisible(this.optionslist))
this.toggle();
else
this.highlightNextOption();
event.stopPropagation();
break;
case 27: // escape
makeInvisible(this.optionslist);
event.stopPropagation();
break;
case 9: // KEY_TAB
case 13: //KEY_ENTER:
if (isVisible(this.optionslist) &&
this._highlighted_node)
{
this.selectOption();
makeInvisible(this.optionslist);
}
event.stopPropagation();
break;
}
},
handleKeyUp: function(event)
{
if (_prevTexteditValue != this.textedit.value)
{
this.exposed_options = [];
this.update();
this.build(this.exposed_options);
}
},
selectOption: function()
{
var tgt = this._highlighted_node;
this.textedit.value = tgt.getAttribute("Desc");
this.value = tgt.getAttribute("Data");
},
moveCaretToEnd: function()
{
var t = this.textedit;
if (t.createTextRange) {
var range = t.createTextRange();
range.collapse(false);
range.select();
} else if (t.setSelectionRange) {
t.focus();
var length = t.value.length;
t.setSelectionRange(length, length);
}
},
update: function()
{
var textedit_value = this.textedit.value.toLowerCase();
var vlen = textedit_value.length
var options = this.config.options;
var get = this.config.optionStringGetter;
for(i=0;i<options.length;i++)
{
var opt_substr =
get(options[i]).toLowerCase().substring(0,vlen)
if (textedit_value == opt_substr)
this.exposed_options.push(options[i]);
}
},
build: function(options)
{
var onmouseover = bind(this.itemMouseOver, this);
var onmouseout = bind(this.itemMouseOut, this);
var divs = [];
for (var i = 0; i < options.length; ++i)
{
var data = options[i];
var string = this.config.optionStringGetter(data);
var div = DIV({'class':'cbox-item',
'onmouseover':onmouseover,
'onmouseout':onmouseout
}, string);
div.setAttribute('Desc', string);
div.setAttribute('Data', data);
divs.push(div);
}
replaceChildNodes(this.optionslist, divs);
makeVisible(this.optionslist);
var visibleCount = Math.min(options.length,
this.config.maxListLength);
if (!visibleCount)
{
makeInvisible(this.optionslist);
this.blurHighlightedNode();
return;
}
var item_dims =
getElementDimensions(this.optionslist.firstChild);
var textedit_dims = getElementDimensions(this.textedit);
var h = visibleCount ? (visibleCount * item_dims.h) : 0;
setElementDimensions(this.optionslist, {w:textedit_dims.w,
h:h});
this.highlightNode(divs[0]);
},
clickOption: function(event)
{
this.highlightNode(event.target());
this.selectOption();
makeInvisible(this.optionslist);
},
toggle: function()
{
if (!this.optionslist || !isVisible(this.optionslist))
{
this.update();
this.build(this.config.options);
this.textedit.focus();
}
else
{
makeInvisible(this.optionslist)
}
},
highlightNode: function(node)
{
this.focusOptionNode(node);
node.scrollIntoView(false);
},
focusOptionNode: function(node)
{
if (this._highlighted_node != node)
{
this.blurHighlightedNode();
this._highlighted_node = node;
addElementClass(this._highlighted_node, "cbox-hilite");
}
},
blurHighlightedNode: function()
{
if (this._highlighted_node)
{
removeElementClass(this._highlighted_node,
"cbox-hilite");
this._highlighted_node = null;
}
},
highlightNextOption: function()
{
if ((!this._highlighted_node) ||
!this._highlighted_node.parentNode)
{
this.focusOptionNode(this.optionsListNode.firstChild);
}
else if (this._highlighted_node.nextSibling)
{
this.focusOptionNode(this._highlighted_node.nextSibling);
}
this._highlighted_node.scrollIntoView(false);
},
highlightPrevOption: function()
{
if (this._highlighted_node &&
this._highlighted_node.previousSibling)
{
this.focusOptionNode(this._highlighted_node.previousSibling);
}
this._highlighted_node.scrollIntoView(false);
},
itemMouseOver: function(event)
{
this.focusOptionNode(event.target);
},
itemMouseOut: function(event)
{
this.blurHighlightedNode();
},
setOptions: function(d)
{
this.config.options = d;
},
setText: function(d)
{
this.textedit.value = d;
}
};
document.write('<link rel="STYLESHEET" type="text/css"
href="my_combobox.css">')
------------------------------
my_combobox.css:
-----------------------------
.cbox-button { cursor:pointer; border:0; padding:0; }
.cbox-item { cursor:pointer; background:white; border:1px solid white;
color: black; font-family:verdana; font-size: 9pt; }
.cbox-hilite { background: rgb(234,242,255); border:1px solid
rgb(120,172,255);}
.cbox-input { border:1px solid rgb(120,172,255) !important;
width:138px !important; vertical-align:baseline; }
.cbox-list { border:1px solid black; background:white; padding:1px;
width:149px; z-index:1000; position:absolute; overflow:auto; }
--------------------------------------------------
my_combobox_sample.html:
--------------------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="visibility.css" />
<link rel="stylesheet" type="text/css" href="my_combobox.css" />
<script type='text/javascript' src='MochiKit/MochiKit.js'></script>
<script type='text/javascript' src='MochiKit/New.js'></script>
<script type='text/javascript' src='MochiKit/Signal.js'></script>
<script type='text/javascript' src='visibility.js'></script>
<script type='text/javascript' src='my_combobox.js'></script>
<script type='text/javascript'>
connect(window, 'onload', init_bu_cbox);
function init_bu_cbox()
{
var d1 = [["argy",1], ["benluc",2], ["benlieeeeck",3], ["baco",4],
["cargo",1], ["senluc",2], ["seeeck",3], ["flaco",4],
["targe",1], ["tux",2], ["tick",3], ["taco",4],
["true",1], ["train",2], ["tried",3], ["telephone",4],
["telepathy",1], ["tink",2], ["tidal",3], ["total",4],
["tuna",1], ["timorous",2], ["tidy",3], ["tipsy",4],
["marge",1], ["men",2], ["menlieeeeck",3], ["maco",4],
["darge",1], ["denluc",2], ["denlieeeeck",3], ["daco",4],
["warge",1], ["genluc",2], ["wenlieeeeck",3], ["waco",4],
["large",1], ["lenluc",2], ["zoo",3], ["lago",4],
];
var d2 = [{code: 'a', desc: 'AAA'},
{code: 'b', desc: 'BBB'},
{code: 'c', desc: 'CCC'},
{code: 'x', desc: 'XXX'},
{code: 'y', desc: 'YYY'},
{code: 'z', desc: 'ZZZ'}];
var flctr_cbox_1 = new My.ComboBox('flctr_cbox_1');
flctr_cbox_1.setOptions(d1);
flctr_cbox_1.setText('charge');
var flctr_cbox_2 = new My.ComboBox('flctr_cbox_2',
{options: d2,
optionStringGetter: itemgetter('desc')
});
var flctr_cbox_3 = new My.ComboBox('flctr_cbox_3',
{ maxListLength: 15});
callLater(3, function() { flctr_cbox_3.setOptions(d1); });
}
</script>
</head>
<body>
<div id="flctr_cbox_1"></div>
<br>
<div id="flctr_cbox_2"></div>
<br>
<div id="flctr_cbox_3"></div>
</body>
</html>
------------------------------
visibility.js
------------------------------
function toggleVisible(elem) {
toggleElementClass("invisible", elem);
}
function makeVisible(elem) {
removeElementClass(elem, "invisible");
}
function makeInvisible(elem) {
addElementClass(elem, "invisible");
}
function isVisible(elem) {
// you may also want to check for
// getElement(elem).style.display == "none"
return !hasElementClass(elem, "invisible");
};
----------------------------
visibility.css
----------------------------
.invisible { display: none; }
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"MochiKit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/mochikit
-~----------~----~----~----~------~----~------~--~---