Turns out you can't smooth scroll for focus changes as the events happen in the wrong order. You get an awful delay while it scrolls the field into view, before focus is set. A better move of the item onscreen, or superfast scroll, is all we can hope for.
As suggested by PlantTheIdea (+1'ed), you need to catch the TAB key and find the next focusable item, bring it into view, then set focus to it.
In practice there are a number of issues to resolve:
- Change of focus occurs on TAB keydown (not keyup).
- Only match non-hidden inputs (lots of web apps have hidden fields that will go bang if you try to focus them).
- Allow for the selection to tab off the first or last item on the page (otherwise the browser loses the ability to tab to its address bar)
- use
e.keyCode || e.which
to allow for older browsers - catch event at document level to allow for cases of other inputs, outside of the scrolling area, causing it to enter the scrolling area (first or last input).
The final code looks like this:
$(document).on('keydown', ':focus', function (event)
{
if ((event.keyCode || event.which) == 9)
{
var $inputs = $(":input:not(hidden)")
var index = $inputs.index(this);
// Index previous or next input based on the shift key
index += event.shiftKey ? -1 : 1;
// If we are in the range of valid inputs (else browser takes focus)
if (index >= 0 && index < $inputs.length)
{
var $next = $inputs.eq(index);
event.preventDefault();
// Move, not scroll, to the next/prev item....
MoveIntoView($next);
$next.focus();
return false;
}
}
});