54

I am having a form with lots of entries. I would like to change my focus to the next textbox, once I entered the value in the current textbox. and want to continue this process up to the last field. My question is, is it possible to simulate what happens when pressing the tab key through Javascript coding once I enter the value in the text box.

Without pressing the tab key in keyboard, I would like to bring the same functionality through Javascript. Is this possible?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
praveenjayapal
  • 37,683
  • 30
  • 72
  • 72

10 Answers10

45

you just need to give focus to the next input field (by invoking focus()method on that input element), for example if you're using jQuery this code will simulate the tab key when enter is pressed:

var inputs = $(':input').keypress(function(e){ 
    if (e.which == 13) {
       e.preventDefault();
       var nextInput = inputs.get(inputs.index(this) + 1);
       if (nextInput) {
          nextInput.focus();
       }
    }
});
krcko
  • 2,834
  • 1
  • 16
  • 11
19

Needed to emulate the tab functionality a while ago, and now I've released it as a library that uses .

EmulateTab: A jQuery plugin to emulate tabbing between elements on a page.

You can see how it works in the demo.

if (myTextHasBeenFilledWithText) {
  // Tab to the next input after #my-text-input
  $("#my-text-input").emulateTab();
}
Joel Purra
  • 24,294
  • 8
  • 60
  • 60
16
function nextField(current){
    for (i = 0; i < current.form.elements.length; i++){
        if (current.form.elements[i].tabIndex - current.tabIndex == 1){
            current.form.elements[i].focus();
            if (current.form.elements[i].type == "text"){
                current.form.elements[i].select();
            }
        }
    }
}

This, when supplied with the current field, will jump focus to the field with the next tab index. Usage would be as follows

<input type="text" onEvent="nextField(this);" />
Ray Foss
  • 3,649
  • 3
  • 30
  • 31
Gausie
  • 4,291
  • 1
  • 25
  • 36
  • 3
    Does not work if tab index is not set manually in HTML. `.tabIndex` property returns 0. – Slava May 29 '17 at 14:24
  • Only works when tabIndex is set to sequential numbers, 1, 2, 3, etc which breaks the tab order of the whole page and does not work well with component based systems (like React) – Paul Smith Oct 18 '22 at 13:53
16

I couldn't find an answer that could do what I wanted. I had a problem where there were some link elements that I didn't want users tabbing to. This is what I came up with:

(Note that in my own code I used a:not(.dropdown-item) instead of a on the allElements line, in order to prevent users tabbing to a.dropdown-item.)

function(event){
        //Note that this doesn't honour tab-indexes

        event.preventDefault();

        //Isolate the node that we're after
        const currentNode = event.target;

        //find all tab-able elements
        const allElements = document.querySelectorAll('input, button, a, area, object, select, textarea, [contenteditable]');

        //Find the current tab index.
        const currentIndex = [...allElements].findIndex(el => currentNode.isEqualNode(el))

        //focus the following element
        const targetIndex = (currentIndex + 1) % allElements.length;
        allElements[targetIndex].focus();
}
Andrew Peters
  • 356
  • 2
  • 6
  • 1
    Fix for "allElements.findIndex is not a function": `const currentIndex = Array.prototype.findIndex.call(allElements, el => currentNode.isEqualNode(el))` – Raine Revere Dec 04 '18 at 22:29
  • 1
    My mistake, I forgot that querySelectorAll returns a NodeList and not an array. – Andrew Peters Dec 05 '18 at 04:21
  • 1
    This is a great method, and worked for my use case. But the new index should be `(currentIndex + 1) % allElements.length`. Modifying it so it respects tabindex is left as an excercise :) – Mason Bourgeois Nov 23 '19 at 07:34
  • @MasonBourgeois I've edited the response to include that. Cheers! – Andrew Peters Nov 23 '19 at 11:19
  • Perfect! If you do not want to change the compatibility compiling options, you can use `Array.from(allElements).findIndex(...)` instead the spread operator used in the answer. – pa4080 Aug 23 '23 at 14:59
6

This simulates a tab through a form and gives focus to the next input when the enter key is pressed.

window.onkeypress = function(e) {
    if (e.which == 13) {
        e.preventDefault();
        var inputs = document.getElementsByTagName('input');
        for (var i = 0; i < inputs.length; i++) {
        if (document.activeElement.id == inputs[i].id && i+1 < inputs.length ) {
            inputs[i+1].focus();
            break;   
        }
    }
            

Updating on the recommendation from @order.

Zach M
  • 427
  • 4
  • 8
1

In the first question, you don't need an event listener on every input that would be wasteful.

Instead, listen for the enter key and to find the currently focused element use document.activeElement

window.onkeypress = function(e) {
    if (e.which == 13) {
       e.preventDefault();
       var nextInput = inputs.get(inputs.index(document.activeElement) + 1);
       if (nextInput) {
          nextInput.focus();
       }
    }
};

One event listener is better than many, especially on low power / mobile browsers.

mattdlockyer
  • 6,984
  • 4
  • 40
  • 44
  • 2
    No next Input ( nextInput ) will be focussed if nextInput disabled, hidden or has a parent which is hidden. Have to perform these checks. – bhavya_w Dec 17 '15 at 07:42
  • The enter key is used to submit forms and other things, so this may lead to some unexpected bugs. – Paul Smith Oct 18 '22 at 13:47
1

In vanilla JS:

function keydownFunc(event) {
      var x = event.keyCode;        
      if (x == 13) {
        try{
            var nextInput = event.target.parentElement.nextElementSibling.childNodes[0];
            nextInput.focus();
          }catch (error){
            console.log(error)
          }
    }
rearThing
  • 632
  • 3
  • 9
  • 26
0

This should work. Working with and without tabindex.

var currentlyFocused = undefined;
var tabableElements = undefined;

/**
 * Compare function for element sort
 * @param {string | null} a
 * @param {string | null} b
 * @param {boolean} asc
 */
function sortCompare(a, b, asc = true) {
  let result = null;
  if (a == null) result = 1;
  else if (b == null) result = -1;
  else if (parseInt(a) > parseInt(b)) result = 1;
  else if (parseInt(a) < parseInt(b)) result = -1;
  else result = 0;
  return result * (asc ? 1 : -1);
}

/**
 * When an element is focused assign it to the currentlyFocused variable
 * @param {Element} element
 */
function registerOnElementFocus(element) {
  element.addEventListener("focus", function(el) {
    currentlyFocused = el.srcElement;
  });
}

/**
 * Tab Trigger
 */
function onTabClick() {
  //Select currently focused element
  let currentIndex;
  tabableElements.forEach((el, idx) => {
    //return if no element is focused
    if (!currentlyFocused) return;
    if (currentlyFocused.isEqualNode(el)) {
      //assign current index and return
      currentIndex = idx;
      return;
    }
  });
  //if theres no focused element or the current focused element is last start over
  let lastIndex = tabableElements.length - 1;
  let nextElementidx = currentIndex === undefined || currentIndex == lastIndex ? 0 : currentIndex + 1;
  //Focus
  currentlyFocused = tabableElements[nextElementidx];
  currentlyFocused.focus();
}
/**
 * Init must be run after all elements are loadead in the dom
 */
function init() {
  //Get all tab-able elements
  let nodeList = document.querySelectorAll("input, button, a, area, object, select, textarea, [tabindex]");
  //To array for easier manipulation
  tabableElements = Array.prototype.slice.call(nodeList, 0);
  //Correcting tabindexes to ensure correct order
  tabableElements.forEach((el, idx, list) => {
    let tabindex = el.getAttribute("tabindex");
    //-1 tabindex will not receive focus
    if (tabindex == -1) list.splice(idx, 1);
    //null or 0 tabindex in normal source order
    else if (tabindex == null || tabindex == 0) {
      list[idx].setAttribute("tabindex", 9999 + idx);
    }
  });
  //sort elements by their tabindex in ascending order
  tabableElements.sort((elementA, elementB) => sortCompare(elementA.getAttribute("tabindex"), elementB.getAttribute("tabindex")));
  //register focus event to elements
  tabableElements.forEach(el => registerOnElementFocus(el));
}
<!doctype html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

  <title>Virtual tab</title>
</head>

<body onload="init()">
  <div class="container">
    <h3>Virtual Tab Demo</h3>
    <form>
      <div class="btn btn-primary" style="position: fixed;" onclick="onTabClick()">
        Tab!
      </div>
      <br>
      <br>
      <button id="button1" type="button" onclick="alert('button 1')">Button1</button>
      <button id="button2" type="button" onclick="alert('button 2')">Button2</button>
      <br>
      <br>
      <input id="text" type='text'>text
      <br>
      <input id="password" type='password' tabindex="-1">password
      <br>
      <input id="number" type='number' tabindex="5">number
      <br>
      <input id="checkbox" type='checkbox'>checkbox
      <br>
      <input id="radio" type='radio'>radio
      <br>
      <br>
      <button id="button3" type="button" onclick="alert('button 3')">Button3</button>
      <button id="button4" type="button" onclick="alert('button 4')">Button4</button>
      <br>
      <br>
      <select id="select">
        <option value="volvo">Volvo</option>
        <option value="saab">Saab</option>
        <option value="mercedes">Mercedes</option>
        <option value="audi">Audi</option>
      </select>
      <br>
      <br> textarea
      <br>
      <textarea id="textarea"></textarea>
        <br>
        <br>
        <span id="span" tabindex="1">Focus other elements.</span>
    </form>
  </div>
  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>

</html>
  • Please try to keep answers short and to the point. Otherwise the relevant code gets lost in the irrelevant code. – Paul Smith Oct 18 '22 at 13:42
0

If you have jQuery UI this little function allows basic tabbing

function handlePseudoTab(direction) {
    if (!document.hasFocus() || !document.activeElement) {
        return;
    }
    const focusList = $(":focusable", $yourHTMLElement);
    const i = focusList.index(document.activeElement);
    if (i < 0) {
        focusList[0].focus(); // nothing is focussed so go to list item 0
    } else if (direction === 'next' && i + 1 < focusList.length) {
        focusList[i + 1].focus(); // advance one
    } else if (direction === 'prev' && i - 1 > -1) {
        focusList[i - 1].focus(); // go back one
    }
}
Theblockbuster1
  • 308
  • 6
  • 15
sidonaldson
  • 24,431
  • 10
  • 56
  • 61
  • 1
    Vanilla JS answers are generally more helpful. The only part of this that is using jQuery is the :focusable selector, replace that selector with vanilla JS and this could be a much more general solution. – Paul Smith Oct 18 '22 at 13:44
-2

I've adapter the answer of ltiong_sh to work for me:

function nextField(current){
    var elements = document.getElementById("my-form").elements;
    var exit = false;
    for(i = 0; i < elements.length; i++){   
        if (exit) {
            elements[i].focus();
            if (elements[i].type == 'text'){
                elements[i].select();
            }   
            break;
        }
        if (elements[i].isEqualNode(current)) { 
            exit = true;
        }       
    }
}