After a bazillion hours of trial and error (which seems to be the ONLY method that works I swear), I got it doing what I want it to... at least, on Firefox. Intense trepidation on testing other browsers. But I think I got somewhere.
To recap: the point of the script was to take a (fake) button, and when it's clicked, show an open select with options. If users are using a mouse, they can choose an option and Something Happens. If they are using the keyboard, however, they should be able to arrow around the select without Something Happening and hit ENTER to make their selection and make Something Happen.
In BOTH cases, if the user simply blurs (leaves the select either by clicking elsewhere on the page or hitting TAB), no selection is made. This allows users to scroll around the options, but choose not to choose one.
HTML:
Code:
<label for="term">An input: </label>
<input type="text" id="term" name="term" autocomplete="off" placeholder="foo bar baz">
<label for="refineSelect" id="refine"><span>Refine term:</span><a href="#void" id="refa"> </a></label>
<select id="refineSelect" name="refineSelect">
<option value="foo">Foo</option>
<option value="bar">Bar</option>
<optgroup label="-- Try on --">
<option value="amazon">Amazon</option>
<option value="bing">Images - Bing</option>
<option value="google">Images - Google</option>
<option value="maps">Maps</option>
<option value="news">News</option>
<option value="wikipedia">Wikipedia</option>
<option value="youtube">YouTube</option>
</optgroup>
<optgroup label="-- Show all --">
<option value="baz">Baz</option>
<option value="quux">Quux</option>
</optgroup>
</select>
CSS:
Code:
#refineSelect {
position: absolute;
right: 0;
top: 35px;
z-index: 4;
}
#refineSelect optgroup, #refineSelect option {
background-color: #fff;
}
#refineSelect option:hover, #refineSelect option:focus {
background-color: #ccc;
}
Javascript:
Code:
(insde object Foo)
init: function () {
var refineAnchor = document.getElementById('refa'),
s = document.getElementById('refineSelect'),
term = document.getElementById('term');
Basis.addClass(s, 'hidden');
s.size = s.length;
//listens for the fake button to be clicked
Basis.addEventListener(refineAnchor,'click',Foo.refineClickListener(s,term));
Basis.addEventListener(s,'blur',Foo.sBlurListener);
...
},
showRefineSelect: function(s,term) {
Basis.removeClass(s,'hidden');
s.focus();
//listen for keyboard
s.onkeydown = (function(s,term) {
return function(e) {
var keyCode = (e.keyCode || e.which);
//only do something when user hits ENTER
if (keyCode && keyCode === 13) {
Foo.termParams(s,term);
}
}
})(s,term);
//listen for mouseclicks
s.onclick = (function(s,term) {
return function() {
Foo.termParams(s,term);
}
})(s,term);
},
termParams: function(s, term) {
term.value = term.value + ' ' + s.options[s.selectedIndex].value;
},
refineClickListener: function(s,term) {
return function() {
Foo.showRefineSelect(s,term);
};
},
hideRefineSelect: function(s) {
Basis.addClass(s, 'hidden');
},
sBlurListener: function(event) {
Foo.hideRefineSelect(this);
}
};
My biggest problem was that I needed
-the functions to run immediately
-the parameters s and term to get passed on to more functions
-the events so I could test for ENTER to prevent up/down arrows from causing the next function to run
(apparently in IE you can use CTRL plus up/down arrows to prevent this, but it's little-known and IE-only anyway)
So, I was only getting the functions to run immediately with crap like
s.onchange = function() {
do stuff;
}
and this let me access the event, but I needed currying.
I had stuff like
s.onchange = function (params) {
return function() {
run stuff
}
}();
but the event kept getting lost.
Sadly, I had to wade through a lot of jQuery-based answers, trying to translate in my head to normal Javascript, where I saw somewhere that I could nest another function in that returned function who doesn't have any params, thus not overwriting my (e). Arg.
Also all the times I tried currying and forgot the "run now" () at the end of the statement, arg.
Well so now anyway, now I has a public record of how I got a goofy UI with a forced-open select to work with both keyboard and mouse without the stupid arrow keys triggering "change" when they shouldn't. It's still not a smart design and not as accessible as I'd like, but, ffff. I'm downing a bottle of whisky anyways.
Bookmarks