24

I have two input elements, the first is focused, and I want to focus the second by simulating the TAB keypress/keydown event. (Note: I don't want to use .next() or such.)

This is my code, inspired from this question:

$('input').first().focus();

var e = $.Event('keydown');

e.which = 9; // TAB
$(':focus').trigger(e);

Please see http://jsfiddle.net/3PcPH/

The code doesn't work. What is wrong?

Community
  • 1
  • 1
Randomblue
  • 112,777
  • 145
  • 353
  • 547

5 Answers5

35

There is no simple programmatic way to do this using Javascript... so here's a brute force way.

According to W3:

Elements that may receive focus should be navigated by user agents according to the following rules:

  1. Those elements that support the tabindex attribute and assign a positive value to it are navigated first. Navigation proceeds from the element with the lowest tabindex value to the element with the highest value. Values need not be sequential nor must they begin with any particular value. Elements that have identical tabindex values should be navigated in the order they appear in the character stream.
  2. Those elements that do not support the tabindex attribute or support it and assign it a value of "0" are navigated next. These elements are navigated in the order they appear in the character stream.
  3. Elements that are disabled do not participate in the tabbing order.

I accomplished this by storing the order of elements in the form that have tabIndex > 0 in their tabIndex order and then concatenating the rest of the elements in the order they appear within the document. The following code simulates a tab keypress when focused on a form input and the letter 'z' is pressed (but you can change this to whatever condition you require):

$(':input').keypress(function(e){ 

    // if 'z' pressed
    if (e.which == 122) {

        // if we haven't stored the tabbing order
        if (!this.form.tabOrder) {

            var els = this.form.elements,
                ti = [],
                rest = [];

            // store all focusable form elements with tabIndex > 0
            for (var i = 0, il = els.length; i < il; i++) {
                if (els[i].tabIndex > 0 &&
                    !els[i].disabled && 
                    !els[i].hidden && 
                    !els[i].readOnly &&
                    els[i].type !== 'hidden') {
                    ti.push(els[i]);
                }
            }

            // sort them by tabIndex order
            ti.sort(function(a,b){ return a.tabIndex - b.tabIndex; });

            // store the rest of the elements in order
            for (i = 0, il = els.length; i < il; i++) {
                if (els[i].tabIndex == 0 &&
                    !els[i].disabled && 
                    !els[i].hidden && 
                    !els[i].readOnly &&
                    els[i].type !== 'hidden') {
                    rest.push(els[i]);
                }
            }

            // store the full tabbing order
            this.form.tabOrder = ti.concat(rest);
        }

        // find the next element in the tabbing order and focus it
        // if the last element of the form then blur
        // (this can be changed to focus the next <form> if any)
        for (var j = 0, jl = this.form.tabOrder.length; j < jl; j++) {
            if (this === this.form.tabOrder[j]) {
                if (j+1 < jl) {
                    $(this.form.tabOrder[j+1]).focus();
                } else {
                    $(this).blur();
                }
            }
        }

    }

});

See demo

mVChr
  • 49,587
  • 11
  • 107
  • 104
  • Thanks for this! I've noticed two discrepancies on Chrome. 1) Using `z` will change the content of the `THIRD` focusable element to `z`. 2) When no element is focused, `TAB` will focus `FIRST` whereas `z` does nothing. – Randomblue Sep 07 '11 at 15:57
  • @Randomblue 1) Pressing 'z' was just an example trigger, I assumed this would be handled programmatically somehow. If needed you can add a `e.preventDefault()`. 2) You can change this by adding the trigger to `$('document')` as well, but from your requirements it sounded like you only wanted this functionality on form inputs. – mVChr Sep 07 '11 at 19:17
  • @Randomblue If you add `return false;` to the last part of the keypress function, it will not enter 'z' into the next textbox as returning false stops the browser from doing anything with the keypress that you first did (But only return false on your captured event otherwise all inputs will be captured) – JakeJ Sep 12 '11 at 08:30
  • 1
    jQuery UI has a [`:tabbable`](http://api.jqueryui.com/tabbable-selector/) and [`:focusable`](http://api.jqueryui.com/focusable-selector/) pseudo-selectors. In case you want to pull something like this in as a library. Should only need jquery-ui.core.js – willscripted Jun 10 '14 at 11:23
  • heads up... `event.which` is now deprecated and should be replaced by `event.key` to check for `Tab`. Read about it on [Mozilla developer pages](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent) – Fr0zenFyr Jan 15 '19 at 04:44
4

The default tabbing behavior is to just go to the next (in source order) form element so you could just iterate through all the elements you care about, find the one that has focus, and move the focus to the next one. We have the :input selector for finding the form elements so something like this:

var $all = $('form :input');
var focused = $(':focus')[0];
for(var i = 0; i < $all.length - 1; ++i) {
    if($all[i] != focused)
        continue;
    $all[i + 1].focus();
    break;
}
// Must have been focused on the last one or none of them.
if(i == $all.length - 1)
    $all[0].focus();

Demo: http://jsfiddle.net/ambiguous/Avugy/1/

Or you could set tabindex attributes and increment them with wrap-around:

var next_idx = parseInt($(':focus').attr('tabindex'), 10) + 1;
var $next_input = $('form [tabindex=' + next_idx + ']');
if($next_input.length)
    $next_input.focus();
else
    $('form [tabindex]:first').focus();

Demo: http://jsfiddle.net/ambiguous/k9VpV/

Dealing with gaps in the tabindex attribute values is left as an exercise.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 1
    Thanks, but I don't like your approaches. Concerning the first, searching through all the elements I care about is *very expensive* (I have many) and is not dynamic (doesn't take into account the new ones I add). Also, I don't know *a priori* what the elements I'm interested in will be. Your second approach has the disadvantages of the first, and needs all the elements to have a different tabindex... – Randomblue Sep 05 '11 at 14:11
0

Here is a solution using jquery to simulate the TAB functionallity with the Enter key:

https://jsfiddle.net/tuho879j/

$('input').keypress(function(event){
  if(event.which == '13')   //ENTER     
  {
    var tabIndex = $(this).attr('tabIndex');

    var all_inputs = $(this).closest('table').find('input:visible');
    var inputs = all_inputs.filter(function() {
      return $(this).attr("tabIndex") > tabIndex;
    })

    if(inputs.length != 0)
    {
        inputs = $(inputs).sort(function(a,b){
          return $(a).attr('tabIndex')-$(b).attr('tabIndex');
        });
    }
    else
    {
        inputs = $(all_inputs).sort(function(a,b){
          return $(a).attr('tabIndex')-$(b).attr('tabIndex');
        });
    }


    var elem = inputs.eq( inputs.index(this)+ 1 );
    if(elem.length == 0)
        elem = inputs.eq(0);

    elem.focus();
    event.preventDefault();
  }
});
burgund
  • 165
  • 1
  • 5
0
$('input').first().focus();

var e = $.Event('keydown');

e.which = 9; // TAB
$(':focus').bind('keydown',function(e){
    if(e.which == 9){
        //this.value="tab";
        $('input:eq(1)').focus();
    }
   e.preventDefault(); 
});

you need to bind 'keydown' event to and custom your event function.

iwege
  • 6,350
  • 1
  • 15
  • 5
  • Thanks. However this end result is not what I want! What is the code I should use so that the next element as determined by `tabindex` will be focused? – Randomblue Sep 05 '11 at 03:25
  • This simply focuses to the second input element. That's *not* as prescribed by `tabIndex`! – Randomblue Sep 05 '11 at 03:42
  • It's not the answer to the actual question, but the google bring me here for my answer, so +1 – electroid May 05 '16 at 04:38
0

Try this sample out http://jsfiddle.net/mporras/3jXQQ/18/embedded/result/

MauroPorras
  • 5,079
  • 5
  • 30
  • 41