Focus next input once reaching maxlength value

95,946

Solution 1

No jQuery used and is a very clean implementation:

  • Reads from the maxlength attribute.
  • Scales to any number of inputs inside of your container.
  • Automatically finds the next input to focus.
  • No jQuery.

http://jsfiddle.net/4m5fg/5/

<div class="container">
a: <input type="text" maxlength="5" />
b: <input type="text" maxlength="5" />
c: <input type="text" maxlength="5" />
</div>

..

var container = document.getElementsByClassName("container")[0];
container.onkeyup = function(e) {
    var target = e.srcElement || e.target;
    var maxLength = parseInt(target.attributes["maxlength"].value, 10);
    var myLength = target.value.length;
    if (myLength >= maxLength) {
        var next = target;
        while (next = next.nextElementSibling) {
            if (next == null)
                break;
            if (next.tagName.toLowerCase() === "input") {
                next.focus();
                break;
            }
        }
    }
    // Move to previous field if empty (user pressed backspace)
    else if (myLength === 0) {
        var previous = target;
        while (previous = previous.previousElementSibling) {
            if (previous == null)
                break;
            if (previous.tagName.toLowerCase() === "input") {
                previous.focus();
                break;
            }
        }
    }
}

Solution 2

You can watch for input in the fields and test its value:

$("input").bind("input", function() {
    var $this = $(this);
    setTimeout(function() {
        if ( $this.val().length >= parseInt($this.attr("maxlength"),10) )
            $this.next("input").focus();
    },0);
});

Working demo.

The setTimeout is there to ensure the code will only run after the input is completed and the value updated. Binding input ensures most types of input will trigger the event, including key presses, copy/paste (even from mouse) and drag & drop (though in this case, the latter won't work, since the focus was on the draggable, not the droppable).

Note: on some older browsers, you might also need to bind propertychange.


If a user pastes text that is greater than the maxlength, ideally it should spill into the next input.

To do that, you might need to remove the maxlength attribute using JavaScript (to be able to capture the full input), and implement that functionality yourself. I made a small example, relevant parts below:

$("input").each(function() {
    var $this = $(this);
    $(this).data("maxlength", $this.prop("maxlength"));
    $(this).removeAttr("maxlength");
})

This removes the attribute, but saves it in data, so you can access it later.

function spill($this, val) {
    var maxlength = $this.data("maxlength");
    if ( val.length >= maxlength ) {
        $this.val(val.substring(0, maxlength));
        var next = $this.next("input").focus();
        spill(next, val.substring(maxlength));
    }
    else
        $this.val(val);
}

Here the max length logic is reintroduced in JavaScript, as well as getting the "discarded" part and using it in a recursive call to spill. If there's no next element, the call to data will return undefined and the loop will stop, so the input will be truncated in the last field.

Solution 3

You can use plain JavaScript:

See DEMO.

Check the character length with el.value.length. If it is equal to the maximum value, move to the next field by using focus(). Bind this function to the keyup event with onkeyup so that the function fires every time after the user keys in a character.

var a = document.getElementById("a"),
    b = document.getElementById("b"),
    c = document.getElementById("c");

a.onkeyup = function() {
    if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
        b.focus();
    }
}

b.onkeyup = function() {
    if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
        c.focus();
    }
}

Solution 4

if you are going to have many fields you can do something like this.

basically on keyup get the length of the input and then compare it to the maxlength, if matches, then focus onto the next input field.

http://jsfiddle.net/btevfik/DVxDA/

$(document).ready(function(){
    $('input').keyup(function(){
        if(this.value.length==$(this).attr("maxlength")){
            $(this).next().focus();
        }
    });
});

Solution 5

let otp = document.querySelector('#otp-screen');

for(let pin of otp.children) {
    pin.onkeyup = function() {
        if(pin.nextElementSibling) {
            pin.nextElementSibling.focus();
        }
    }
}
<div class="otp-screen" id="otp-screen">
    <input type="text" placeholder="0" maxlength="1"/> 
    <input type="text" placeholder="0" maxlength="1"/> 
    <input type="text" placeholder="0" maxlength="1"/> 
    <input type="text" placeholder="0" maxlength="1"/> 
</div>
Share:
95,946
O P
Author by

O P

Updated on July 09, 2022

Comments

  • O P
    O P almost 2 years

    How can I focus the next input once the previous input has reached its maxlength value?

    a: <input type="text" maxlength="5" />
    b: <input type="text" maxlength="5" />
    c: <input type="text" maxlength="5" />
    

    If a user pastes text that is greater than the maxlength, ideally it should spill into the next input.

    jsFiddle: http://jsfiddle.net/4m5fg/1/

    I must stress that I do not want to use a plugin, as I'd much rather learn the logic behind this, than use something that already exists. Thanks for understanding.

  • exexzian
    exexzian about 11 years
    your fiddle works perfect and +1 for that but, is binding propertyChange mandatory ? it seems to work fine even without that
  • mgibsonbr
    mgibsonbr about 11 years
    @Bingo According to this answer, input is for Firefox and propertychange for the others, but things might have changed since then (i.e. maybe every browser supports input now). The only way to be sure is testing on different environments. (P.S. it's propertychange, not propertyChange - I already corrected my answer)
  • Mir
    Mir over 10 years
    This only works for the first container in the page. It's relatively trivial to modify it to work for all of them, but it's worth pointing out in case novices come to use it.
  • Cristi Draghici
    Cristi Draghici almost 8 years
    var target = e.srcElement || e.target; helped me use your solution. Thank you!
  • Nuno cruz
    Nuno cruz over 7 years
    This should be the correct answer. If you add maxlength = 2, on the accepted answer you cannot edit the inputs but in this one you can!
  • Nuno cruz
    Nuno cruz over 7 years
    If you add maxlength = 2 you cannot edit the input fields.
  • oriaj
    oriaj over 7 years
    this is a good alternative but have problems, for example if you have each input in a different div doesn't work
  • agon024
    agon024 over 6 years
    Ya this doesn't work if you have the input fields in seperate divs or if you have a div say containing a dash ( - ) in between the input fields.
  • mgibsonbr
    mgibsonbr over 6 years
    That's true, however the code needs one way to specify what the "next input" is - you can't just search for any input in the page and use that, since this behavior might lead to problems... The overall code is fine, just the selection part (in this case, the .next call) that needs to be adapted to each particular case (for instance a .nextAll if they are all in the same div, or a .parent().next().find if they are each in its own div, etc).
  • Srdjan Dejanovic
    Srdjan Dejanovic over 6 years
    bind is deprecated, use on
  • Surya R Praveen
    Surya R Praveen over 6 years
    Using keyup function might create issue, For e.g. you have to delete some number inside textbox, that event will not allow to delete as it's a keypress. Instead use oninput event. It will be helpful
  • Surya R Praveen
    Surya R Praveen over 6 years
    Updated your code for better solution - jsfiddle.net/ssuryar/DVxDA/224 - DEMO
  • Gleb Sabirzyanov
    Gleb Sabirzyanov about 5 years
    I had to change maxLength to target.maxLength instead of parseInt(target.attributes["maxlength"].value, 10) in order to make it work. However, I'm using the proper listener instead of redefining the onkeyup function: container.addEventListener('keyup', moveToNext) (where moveToNext is the function from the above answer).
  • icortesi
    icortesi over 4 years
    This worked great for me, I also modified it so it will go to the previous field when you delete everything from the focused field.
  • Vitor Hugo
    Vitor Hugo almost 4 years
    @icortesi, you could share this modification, it would be very useful for other people.
  • Lajos Arpad
    Lajos Arpad almost 4 years
    This only works with a maxlength of 1. The question is clearly not assuming such a maxlength.
  • kkasp
    kkasp almost 2 years