6

I'm making a single page app that is launching next week, for a pretty huge client, and going live for a pretty big event and well, there's still a ton to finish before then.

There's 100+ 'pages' which are all loaded within a single 700px x 600px window, and I had learned not long ago you could tab through the page/sections, which in-turn would break the app because it would bring focus to hidden off-screen elements, so for this reason, I disabled the tab key for the entire app.

But now there are a couple places where we have a form with a handful of input fields which you are not able to tab through as you fill in the form. It's a pain in the ass.

I need to make it so you can tab through the form fields, but only the form fields. I have the tabindex attribute set for the form, and have tried to make inputs tab enabled but was not able to make it work without causing the app to jump to hidden sections.

Here's the function I need to change so it will disable tab key except from input to input fields in a form.

window.onkeydown = function(e) {
    if (e.keyCode === tab) {
        return false;
    }
}

I tried to do this, which obv didnt work lol

$('input').keydown(function(e) {
    if (e.keyCode === tab) {
        return true;
    }
});

Thanks :)

jaredwilli
  • 11,762
  • 6
  • 42
  • 41
  • are you still planning to post your solution as you indicated in a comment? – psr Jun 01 '12 at 00:41
  • I actually never did get around to finding a solution. But, just now saw this comment and came up with what is a complete solution for tabbing and shift+tabbing on forms and have posted it below. – jaredwilli Oct 16 '12 at 22:59

3 Answers3

10

I made some fixes to what @Joseph posted for an answer to this that handle being able to shift + tab through inputs of a form so you can reverse direction. It was a very annoying thing for me before when I first had to find a way to do this, and didn't have time to waste anymore trying to find a complete solution for this until now. Here it is.

$(function() {
    // gather all inputs of selected types
    var inputs = $('input, textarea, select, button'), inputTo;

    // bind on keydown
    inputs.on('keydown', function(e) {

        // if we pressed the tab
        if (e.keyCode == 9 || e.which == 9) {
            // prevent default tab action
            e.preventDefault();

            if (e.shiftKey) {
                // get previous input based on the current input
                inputTo = inputs.get(inputs.index(this) - 1);
            } else {
                // get next input based on the current input
                inputTo = inputs.get(inputs.index(this) + 1);
            }

            // move focus to inputTo, otherwise focus first input
            if (inputTo) {
                inputTo.focus();
            } else {
                inputs[0].focus();
            }
        }
    });
});

Demo of it working http://jsfiddle.net/jaredwilli/JdJPs/

jaredwilli
  • 11,762
  • 6
  • 42
  • 41
4

Have you tried setting tabIndex="-1" on all elements that you don't want to be able to tab to? I think that's a much better solution.

Otherwise, within your key handler function test event.target (or event.srcElement in IE) to see if the event originated with a form element. You seem to be using jQuery, so you could assign an "allowTab" class just to the fields in your form and then do this:

$(document).keydown(function(e) {
   if (!$(e.target).hasClass("allowTab"))
      return false;
});

Or

if (e.target.tagName !== "input")
// etc
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • I expect the OP will need to set a `tabIndex` > -1 only for those fields that are in the currrent "screen" and should be navigable, everything else should be -1. – RobG Mar 02 '12 at 02:24
  • @RobG - Sure. That's what I meant in my first sentence. Easy enough (especially since he seems to be using jQuery) to change the tab indexes when the "screen" changes. My main point is to use `tabIndex` for this rather than trying to cancel the tab keypress. – nnnnnn Mar 02 '12 at 02:27
  • There are dozens of elements that would need to have a tabindex=-1. I also should mention the fact that we are cloning and swapping whole sections of html code in and out of containers depending on what context your in by what you've navigated to. it's quite an extremely complex thing, but it creates an issue that involves needing this code to be available whenever the elements are in the DOM in any given context. Its not always a case that can be run whenever and have it work out... – jaredwilli Mar 02 '12 at 05:01
  • Sorry, I guess I just don't understand your page structure. I would think that since you're using jQuery you could easily set the tabindex for all (appropriate) elements with one line of code. Then, surely, you know when you are displaying the form and at that point you can set its elements' tabindexes back to 0? – nnnnnn Mar 02 '12 at 05:18
  • nnnnnn—I don't think being able to do it one line or two is the issue. Perhaps a single keypress/down whatever listener on the document will do. If a tab press comes from an element that should allow it, allow it. Otherwise cancel it. All that is required in support is a list of the elements (ids, class, members of an array, whatever) that should allow tab navigation for the current page "context". – RobG Mar 03 '12 at 09:39
4

what we do is to determine what input is next in line and skip to it!:

http://jsfiddle.net/qXDvd/

$(document).ready(function() {

    //gather all inputs of selected types
    var inputs = $('input, textarea, select, button');

    //bind on keydown
    inputs.on('keydown', function(e) {

        //if we pressed the tab
        if (e.keyCode == 9 || e.which == 9) {

            //prevent default tab action
            e.preventDefault();

            //get next input based on the current input we left off
            var nextInput = inputs.get(inputs.index(this) + 1);

            //if we have a next input, go to it. or go back
            if (nextInput) {
                nextInput.focus();
            }
            else{
                inputs[0].focus();
            }
        }
    });
});​

may need some optimization but it works. this was originally meant to skip non-form elements. you can add selectors not to skip if you like. additionally, you can add logic for the Shift+Tab behavior (maybe before the tab logic)


obviously, it will still go through some elements according to how they appear in the source. however, why not just remove those hidden elements from the DOM but still keep track of them using the methods found in this question. that way, you won't have the pain of having to cycle back and forth through off-screen elements.

Community
  • 1
  • 1
Joseph
  • 117,725
  • 30
  • 181
  • 234
  • This doesn't skip hidden inputs, or inputs set off-screen as mentioned in the question, plus it skips links such that you can't use the page if you don't (or can't) use a mouse or other pointing device, also (though obviously this is easy to fix) it loops _forwards_ only, breaking the default shift-tab behaviour. – nnnnnn Mar 02 '12 at 03:14
  • why not remove those offscreen elements or atleast store some references to it using DOM fragments or clones if you want to reuse them. – Joseph Mar 02 '12 at 03:23
  • A slighly modified version of this, when used within each of the contexts of the app where there are form fields that should be tabbable (within the panel setup functions that contain forms) seems to work so far. The code needs to be in all the setup functions containing forms, because until the functions are run, they don't exist in the DOM, and when the context changes, they are removed from the DOM, making it necessary to have it within the context that has forms. Thanks :) – jaredwilli Mar 02 '12 at 12:20
  • I came to find that while this way works, it doesn't handle reverse tabbing through the fields, shift+tab. If it wasn't for the fact that I've had to test and re-test and tessstt this form thing that Im making so many times, it was bound to come up for me eventually. So, once I write the shift+tab part for this to truly simulate the tab key, i'll post it here – jaredwilli Mar 06 '12 at 01:45