Related
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.
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;
}
}
}
}
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.
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();
}
}
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();
}
});
});
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>
Updated btevfik code, Onkeyup or onkeydown will create issue as you won't be able to delete the previous input on tab navigation. It will be tough to edit or change the text inside the input box as it will be limited to maxlength. So we can use oninput event to achieve the task.
DEMO
HTML
<ul>
<li>a: <input type="text" maxlength="5" /></li>
<li>b: <input type="text" maxlength="3" /></li>
<li>c: <input type="text" maxlength="5" /></li>
<li>d: <input type="text" maxlength="3" /></li>
<li>e: <input type="text" maxlength="6" /></li>
<li>f: <input type="text" maxlength="10" /></li>
<li>g: <input type="text" maxlength="7" /></li>
</ul>
Javascript
$(document).ready(function(){
$('input').on("input", function(){
if($(this).val().length==$(this).attr("maxlength")){
$(this).next().focus();
}
});
});
CSS
ul {list-style-type:none;}
li {padding:5px 5px;}
Other answers do give some idea how this can be implemented, but I find that they do not consider some minor things among which are:
The fact, that you do not want to auto-focus any elements across whole page, but rather within specific form.
Input elements can be wrapped in some other elements (for example I wrap them in span or div to allow floating labels through CSS, and I've seen forms that use table to for structure).
Validity of the field, when spilling over or moving to next one automatically.
Input events when spilling over.
Cursor position when returning to previous field (it looks like it can be saved by browser, thus backspacing can focus not in the end of the field, but, for example, in the middle).
Below code is trying to account to all of this, at least. Most of it can be tested on codepen: paste-spilling does not work there, looks like because of Clipboard API (other codepens with it do not work for me either).
Let me know if anything is unclear in the code, I'll update my answer and the code. If you find some edge case that is not covered - let me know as well.
For paste-spilling test using form from codepen, you can use something like this: 123456789123456789012345678903454353434534
Video sample of how it works in a more "live" envitonment on youtube
//List of input types, that are "textual" by default, thus can be tracked through keypress and paste events. In essence,
// these are types, that support maxlength attribute
const textInputTypes = ['email', 'password', 'search', 'tel', 'text', 'url', ];
formInit();
//Add listeners
function formInit()
{
document.querySelectorAll('form input').forEach((item)=>{
if (textInputTypes.includes(item.type)) {
//Somehow backspace can be tracked only on keydown, not keypress
item.addEventListener('keydown', inputBackSpace);
if (item.getAttribute('maxlength')) {
item.addEventListener('input', autoNext);
item.addEventListener('change', autoNext);
item.addEventListener('paste', pasteSplit);
}
}
});
}
//Track backspace and focus previous input field, if input is empty, when it's pressed
function inputBackSpace(event)
{
let current = event.target;
if ((event.keyCode || event.charCode || 0) === 8 && !current.value) {
let moveTo = nextInput(current, true);
if (moveTo) {
moveTo.focus();
//Ensure, that cursor ends up at the end of the previous field
moveTo.selectionStart = moveTo.selectionEnd = moveTo.value.length;
}
}
}
//Focus next field, if current is filled to the brim and valid
function autoNext(event)
{
let current = event.target;
//Get length attribute
let maxLength = parseInt(current.getAttribute('maxlength'));
//Check it against value length
if (maxLength && current.value.length === maxLength && current.validity.valid) {
let moveTo = nextInput(current, false);
if (moveTo) {
moveTo.focus();
}
}
}
async function pasteSplit(event)
{
let permission = await navigator.permissions.query({ name: 'clipboard-read',});
//Check permission is granted or not
if (permission.state === 'denied') {
//It's explicitly denied, thus cancelling action
return false;
}
//Get buffer
navigator.clipboard.readText().then(result => {
let buffer = result.toString();
//Get initial element
let current = event.target;
//Get initial length attribute
let maxLength = parseInt(current.getAttribute('maxlength'));
//Loop while the buffer is too large
while (current && maxLength && buffer.length > maxLength) {
//Ensure input value is updated
current.value = buffer.substring(0, maxLength);
//Trigger input event to bubble any bound events
current.dispatchEvent(new Event('input', {
bubbles: true,
cancelable: true,
}));
//Do not spill over if a field is invalid
if (!current.validity.valid) {
return false;
}
//Update buffer value (not the buffer itself)
buffer = buffer.substring(maxLength);
//Get next node
current = nextInput(current);
if (current) {
//Focus to provide visual identification of a switch
current.focus();
//Update maxLength
maxLength = parseInt(current.getAttribute('maxlength'));
}
}
//Check if we still have a valid node
if (current) {
//Dump everything we can from leftovers
current.value = buffer;
//Trigger input event to bubble any bound events
current.dispatchEvent(new Event('input', {
bubbles: true,
cancelable: true,
}));
}
}).catch(err => {
//Most likely user denied request. Check status
navigator.permissions.query({ name: 'clipboard-read',}).then(newPerm => {
if (newPerm.state === 'granted') {
console.log('Failed to read clipboard', err);
} else {
console.log('Request denied by user. Show him some notification to explain why enabling permission may be useful');
}
}).catch(errPerm => {
console.log('Failed to read clipboard', errPerm);
});
});
}
//Find next/previous input
function nextInput(initial, reverse = false)
{
//Get form
let form = initial.form;
//Iterate inputs inside the form. Not using previousElementSibling, because next/previous input may not be a sibling on the same level
if (form) {
let previous;
for (let moveTo of form.querySelectorAll('input')) {
if (reverse) {
//Check if current element in loop is the initial one, meaning
if (moveTo === initial) {
//If previous is not empty - share it. Otherwise - false, since initial input is first in the form
if (previous) {
return previous;
} else {
return false;
}
}
} else {
//If we are moving forward and initial node is the previous one
if (previous === initial) {
return moveTo;
}
}
//Update previous input
previous = moveTo;
}
}
return false;
}
If you are adding input text fields dynamically then you can try this.
This will re-inject the script into the DOM and works Perfectly.
$('body').on('keyup', '#num_1',function(){
if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
$('#num_2').focus();
}
})
$('body').on('keyup','#num_2', function(){
if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
$('#num_3').focus();
}
})
<input type="text" class="form-control" name="number" maxlength="3" id="num_1">
<input type="text" class="form-control" name="number" maxlength="3" id="num_2">
<input type="text" class="form-control" name="number" maxlength="4" id="num_3">
If you're focused on creating card(debit/credit) number input type. Then clean an easily manageable jQuery version as follows:
/*..............................................................................................
* jQuery function for Credit card number input group
......................................................................................................*/
// make container label of input groups, responsible
$('.card-box').on('focus', function(e){
$(this).parent().addClass('focus-form-control');
});
$('.card-box').on('blur', function(e){
$(this).parent().removeClass('focus-form-control');
});
$('.card-box-1').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-2').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
return;
}
});
$('.card-box-2').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-3').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
$('.card-box-1').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
$('.card-box-3').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-4').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
$('.card-box-2').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
$('.card-box-4').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
return;
}
if(_length <= 0){
$('.card-box-3').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
/*..............................................................................................
* End jQuery function for Credit card number input group
......................................................................................................*/
/* Hide HTML5 Up and Down arrows. */
input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none; margin: 0;
}
input[type="number"] { -moz-appearance: textfield; }
.card-box {
width: 20%; display: inline-block; height: 100%; border: none;
}
.focus-form-control {
border-color: #66afe9; outline: 0;-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="form-control" style="padding: 0; max-width: 300px; ">
<input class="card-box card-box-1" type="number" id="CreditCard_CardNumber1" required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;"/>
<input class="card-box card-box-2" type="number" id="CreditCard_CardNumber2" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
<input class="card-box card-box-3" type="number" id="CreditCard_CardNumber3" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
<input class="card-box card-box-4" type="number" id="CreditCard_CardNumber4" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
</div>
Verified Answer have one issue which focus previous field if previous field have valid length
I have Modified Above Answer to fix complete length of previous tag
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;
// Move to previous field if backspace is pressed
if (code == 8) {
previous = previous.previousElementSibling;
if (previous != null) {
if (previous.tagName.toLowerCase() === "input") {
previous.focus();
}
}
} else {
while (previous = previous.previousElementSibling) {
if (previous == null)
break;
if (previous.tagName.toLowerCase() === "input") {
var mLength = parseInt(previous.attributes["maxlength"].value, 10);
var pMyLength = previous.value.length;
// Move to previous field if it does not have required length
if (mLength == pMyLength) {
break;
} else {
previous.focus();
break;
}
}
}
}
}
}
I think this is a shorter way. As long as you use a specific structure in HTML
const inputHandler = (event) => {
if (event.target.nodeName == "INPUT" && event.target.nextElementSibling != null) {
event.target.nextElementSibling.focus();
}
}
<div class="enter-code">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
</div>
I am trying to make a simple calculator. You enter one number, you enter the second one, press PLUS and get alert with an answer. I need to show alert('no data') if you click on PLUS when input fields are not touched.
function num1() {
nm = document.getElementById('nsum1').value;
}
function num2() {
mn = document.getElementById('nsum2').value;
}
function plus() {
sum = +nm + +mn;
if (nm == null || mn == null) {
alert('no data');
} else {
alert(sum);
}
}
<input onchange="num1()" id="nsum1" name="numb" type="tel" placeholder="number" maxlength="6" />
<span onclick="plus()" id="sum">PLUS</span>
<input onchange="num2()" id="nsum2" name="numb" type="tel" placeholder="number" maxlength="6" />
So far I have tried if(sum == undefined)/if(sum == null)/if(sum == false)/if(isNaN(sum))/if(sum == "") and nothing seems to work.
If you haven't touched the input field and get the value, then the result would be ""
You need a condition like
if (nm == "" || mn == "") {
alert('no data');
}
And also you should do operation after validations. You are doing operation and then validating.
Fixed other issues aswell.
function plus() {
mn = document.getElementById('nsum2').value;
nm = document.getElementById('nsum1').value;
if (nm == "" || mn == "") {
alert('no data');
} else {
sum = +nm + +mn;
alert(sum);
}
}
<input id="nsum1" name="numb" type="tel" placeholder="number" maxlength="6" />
<span onclick="plus()" id="sum">PLUS</span>
<input id="nsum2" name="numb" type="tel" placeholder="number" maxlength="6" />
You can do it much easier
function plus(num1, num2) {
alert(isNaN(num1) || isNaN(num2) ? 'No data' : num1 + num2);
}
function getNumber(id) {
return parseFloat(document.getElementById(id).value);
}
<input id="nsum1" type="number" placeholder="number" maxlength="6" />
<span onclick="plus(getNumber('nsum1'), getNumber('nsum2'))" id="sum">PLUS</span>
<input id="nsum2" type="number" placeholder="number" maxlength="6" />
I've made some changes to your code to make it more robust. See the inline comments for a description.
Declare variables
It is important to declare your variables, when you don't all the variables you are using will wind up in the global scope. When you Google this you will find many articles like this one: https://gist.github.com/hallettj/64478.
Prevent polluting the global scope. In a small website this may not be much of an issue but when working on larger project or with third party code, this is a must. The link above also explains this to some extend.
Use a button If you want something to be interactive, use an HTML element that was meant for it. The button element should be used, it has all sorts of accessibility features the span doesn't have. For instance, by default it will receive focus when navigating your website with the tab key.
Use descriptive variable names nm and mn may mean something to you now but in 6 months it will be a complete mystery. It also makes the code more readable and thus easier to maintain.
Attach event listeners in JS In general it is a bad idea to assign event listeners through the HTML attribute onXXX="". It is more error prone and a lot more time intensive when you want to change something.
// Wrap your code in a closure to prevent poluting the global scope.
(function() {
// Always declare your variables. These variables are no longer scoped to the window object but are no scoped to the function we're in.
var
valueA = null,
valueB = null;
/**
* To check if your input is a valid number requires a couple of checks.
* It is best to place these into their own method so you're other
* method is more readable.
*/
function isNumber(value) {
if (
// == null checks for undefined and null
value == null ||
value === '' ||
isNaN(value)
) {
return false;
}
return true;
}
function onChangeHandler(event) {
var
// Get the element that dispatched the event.
target = event.target;
// Check if the target has the class we've assigned to the inputs, of not you can ignore the event.
if (!target.classList.contains('js-input')) {
return;
}
// Based on the ID of the target, assign the value to one of the variables for the values.
switch(target.id) {
case 'nsum1':
valueA = parseFloat(target.value);
break;
case 'nsum2':
valueB = parseFloat(target.value);
break;
}
}
function onSumTriggerClicked(event) {
// Check if there are numbers to work with
if (
!isNumber(valueA) ||
!isNumber(valueB)
) {
// If not alert the user
alert('no data');
return;
}
sum = valueA + valueB;
alert(sum);
}
function init() {
var
// Get the calculator element.
calculator = document.getElementById('calculator'),
// Get the button to sum up the value.
sumButton = document.getElementById('sum-trigger');
// Add an event listener for the change event.
calculator.addEventListener('change', onChangeHandler);
// Add an event listener for the click event.
sumButton.addEventListener('click', onSumTriggerClicked);
}
// Call the init method.
init();
})();
<div id="calculator">
<input class="js-input" id="nsum1" name="numb" type="tel" placeholder="number" maxlength="6" />
<button type="button" id="sum-trigger" id="sum">PLUS</button>
<input class="js-input" id="nsum2" name="numb" type="tel" placeholder="number" maxlength="6" />
</div>
Try to track it via Inspector, maybe log the values of nm and mn before anything else and correct your condition accordingly(as the sample).
function plus() {
console.log(nm);
sum = +nm + +mn;
if (nm == null || mn == null) {
alert('no data');
}
It will most likely just be blank. So in this case you can modify your condition into:
if (nm === '' || mn === '') {...}
Hope it will help
Please use this as reference.
I've fixed your code.
if ( num1 === '' && num2 === '' ) {
alert('no data');
} else {
alert( parseInt(num1) + parseInt(num2) );
}
I am trying to add a text box which accepts only integers between 1 and 99. I tried adding a number type element with min and max but that works only when changing the number using the ticker
<input type="number" min="1" max="99" />
May I know a better way to achieve this?
UPDATE:
I would like to immediately validate the input like may be by replacing the entry with an empty character.
You can use JavaScript to validate the input on a key event (such as keyup). Here's an example that will clear out anything that's not an integer between 1 and 99.
var inputBox = document.getElementById('inputBox');
inputBox.addEventListener('keyup', function(e) {
inputBox.value = "" + inputBox.value;
if (e.which < 48 || e.which > 57) {
// if not an integer
clearInput();
return;
}
if (inputBox.value.toLowerCase() !== inputBox.value.toUpperCase()) {
// if a letter
clearInput();
return;
}
if (parseInt(inputBox.value) < 1 || parseInt(inputBox.value) > 99) {
// if value is not in the desired range
clearInput();
return;
}
});
function clearInput() {
// invalid entry -- clear out input box
inputBox.value = "";
}
inputBox.focus(); // this is just for ease of testing
<input id="inputBox" type="number" min="1" max="99" />
How about this? This shouldn't allow for negative numbers, and you're restricted to only 2 characters.
<input type="number" min="1" max="99" maxlength="2" />
Code:
<input type="text" onkeydown="
if (event.keyCode == 13) { HERE, ECHO "OK" RIGHT AFTER THIS INPUT ELEMENT }
">
Is this possible to do without putting an ID or name on this element?
Or without encasing it in an identifiable div?
To clarify, this should be the result html after the event keycode is pressed:
<input type="text" onkeydown="
if (event.keyCode == 13) { HERE, ECHO "OK" RIGHT AFTER THIS INPUT ELEMENT }
">OK
If you want to use jQuery you can do the following:
HTML
<input name="ok" type="text" id="showOK" />
JAVASCRIPT
$("#showOK").keypress(function() {
if (event.keyCode == 13){
$("<p>OK</p>").insertAfter("#showOK");
}
});
Without the Use of ID
<input type="text" onkeydown="if (event.keyCode == 13) { $('<p>OK</p>').insertAfter(this);}">
Following your requirements and trying to keep your style, you can use the insertAdjacentHTML DOM Element method to add a text just after the input element.
<input type="text" onkeydown="
if (event.keyCode == 13) { this.insertAdjacentHTML('afterend', 'OK');}
">
See demo
I used a mixture of answers here.
First off i had to create a new var:
var that = this;
Or else javascript could not find "this" somehow.
Then used the jQUery method:
$('<span>OK</span>').insertAfter(that);
Resulting in:
<input type="text" onkeydown="
if (event.keyCode == 13) { var that = this; $('<span>OK</span>').insertAfter(that); }
">
Ok, first off, don't use inline-js. But to answer your question,
<input type="text" onkeydown="
if (window.event.keyCode == 13) { this.outerHTML = this.outerHTML + 'Ok'; } "/>
DEMO
Try this:
<input type="text" onkeydown="(function(event){
var input = event.target;
if(event.keyCode == 13){
input.outerHTML += 'OK';
}
})(window.event);" />
Demo.
<input type="text" onkeydown="ok(this, event)"/>
function ok(el, event) {
var _ok = document.createElement('span');
_ok.textContent = 'ok';
if (event.which == 13) {
el.parentNode.insertBefore(_ok, el.nextSibling)
}
}
Try this:
<input type="text" onkeydown="
if (event.keyCode == 13) { this.parentNode.insertBefore(document.createTextNode('OK'), this.nextElementSibling); }
">
It will add a text node (without creating any span or div) directly after the input.
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.
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;
}
}
}
}
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.
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();
}
}
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();
}
});
});
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>
Updated btevfik code, Onkeyup or onkeydown will create issue as you won't be able to delete the previous input on tab navigation. It will be tough to edit or change the text inside the input box as it will be limited to maxlength. So we can use oninput event to achieve the task.
DEMO
HTML
<ul>
<li>a: <input type="text" maxlength="5" /></li>
<li>b: <input type="text" maxlength="3" /></li>
<li>c: <input type="text" maxlength="5" /></li>
<li>d: <input type="text" maxlength="3" /></li>
<li>e: <input type="text" maxlength="6" /></li>
<li>f: <input type="text" maxlength="10" /></li>
<li>g: <input type="text" maxlength="7" /></li>
</ul>
Javascript
$(document).ready(function(){
$('input').on("input", function(){
if($(this).val().length==$(this).attr("maxlength")){
$(this).next().focus();
}
});
});
CSS
ul {list-style-type:none;}
li {padding:5px 5px;}
Other answers do give some idea how this can be implemented, but I find that they do not consider some minor things among which are:
The fact, that you do not want to auto-focus any elements across whole page, but rather within specific form.
Input elements can be wrapped in some other elements (for example I wrap them in span or div to allow floating labels through CSS, and I've seen forms that use table to for structure).
Validity of the field, when spilling over or moving to next one automatically.
Input events when spilling over.
Cursor position when returning to previous field (it looks like it can be saved by browser, thus backspacing can focus not in the end of the field, but, for example, in the middle).
Below code is trying to account to all of this, at least. Most of it can be tested on codepen: paste-spilling does not work there, looks like because of Clipboard API (other codepens with it do not work for me either).
Let me know if anything is unclear in the code, I'll update my answer and the code. If you find some edge case that is not covered - let me know as well.
For paste-spilling test using form from codepen, you can use something like this: 123456789123456789012345678903454353434534
Video sample of how it works in a more "live" envitonment on youtube
//List of input types, that are "textual" by default, thus can be tracked through keypress and paste events. In essence,
// these are types, that support maxlength attribute
const textInputTypes = ['email', 'password', 'search', 'tel', 'text', 'url', ];
formInit();
//Add listeners
function formInit()
{
document.querySelectorAll('form input').forEach((item)=>{
if (textInputTypes.includes(item.type)) {
//Somehow backspace can be tracked only on keydown, not keypress
item.addEventListener('keydown', inputBackSpace);
if (item.getAttribute('maxlength')) {
item.addEventListener('input', autoNext);
item.addEventListener('change', autoNext);
item.addEventListener('paste', pasteSplit);
}
}
});
}
//Track backspace and focus previous input field, if input is empty, when it's pressed
function inputBackSpace(event)
{
let current = event.target;
if ((event.keyCode || event.charCode || 0) === 8 && !current.value) {
let moveTo = nextInput(current, true);
if (moveTo) {
moveTo.focus();
//Ensure, that cursor ends up at the end of the previous field
moveTo.selectionStart = moveTo.selectionEnd = moveTo.value.length;
}
}
}
//Focus next field, if current is filled to the brim and valid
function autoNext(event)
{
let current = event.target;
//Get length attribute
let maxLength = parseInt(current.getAttribute('maxlength'));
//Check it against value length
if (maxLength && current.value.length === maxLength && current.validity.valid) {
let moveTo = nextInput(current, false);
if (moveTo) {
moveTo.focus();
}
}
}
async function pasteSplit(event)
{
let permission = await navigator.permissions.query({ name: 'clipboard-read',});
//Check permission is granted or not
if (permission.state === 'denied') {
//It's explicitly denied, thus cancelling action
return false;
}
//Get buffer
navigator.clipboard.readText().then(result => {
let buffer = result.toString();
//Get initial element
let current = event.target;
//Get initial length attribute
let maxLength = parseInt(current.getAttribute('maxlength'));
//Loop while the buffer is too large
while (current && maxLength && buffer.length > maxLength) {
//Ensure input value is updated
current.value = buffer.substring(0, maxLength);
//Trigger input event to bubble any bound events
current.dispatchEvent(new Event('input', {
bubbles: true,
cancelable: true,
}));
//Do not spill over if a field is invalid
if (!current.validity.valid) {
return false;
}
//Update buffer value (not the buffer itself)
buffer = buffer.substring(maxLength);
//Get next node
current = nextInput(current);
if (current) {
//Focus to provide visual identification of a switch
current.focus();
//Update maxLength
maxLength = parseInt(current.getAttribute('maxlength'));
}
}
//Check if we still have a valid node
if (current) {
//Dump everything we can from leftovers
current.value = buffer;
//Trigger input event to bubble any bound events
current.dispatchEvent(new Event('input', {
bubbles: true,
cancelable: true,
}));
}
}).catch(err => {
//Most likely user denied request. Check status
navigator.permissions.query({ name: 'clipboard-read',}).then(newPerm => {
if (newPerm.state === 'granted') {
console.log('Failed to read clipboard', err);
} else {
console.log('Request denied by user. Show him some notification to explain why enabling permission may be useful');
}
}).catch(errPerm => {
console.log('Failed to read clipboard', errPerm);
});
});
}
//Find next/previous input
function nextInput(initial, reverse = false)
{
//Get form
let form = initial.form;
//Iterate inputs inside the form. Not using previousElementSibling, because next/previous input may not be a sibling on the same level
if (form) {
let previous;
for (let moveTo of form.querySelectorAll('input')) {
if (reverse) {
//Check if current element in loop is the initial one, meaning
if (moveTo === initial) {
//If previous is not empty - share it. Otherwise - false, since initial input is first in the form
if (previous) {
return previous;
} else {
return false;
}
}
} else {
//If we are moving forward and initial node is the previous one
if (previous === initial) {
return moveTo;
}
}
//Update previous input
previous = moveTo;
}
}
return false;
}
If you are adding input text fields dynamically then you can try this.
This will re-inject the script into the DOM and works Perfectly.
$('body').on('keyup', '#num_1',function(){
if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
$('#num_2').focus();
}
})
$('body').on('keyup','#num_2', function(){
if (this.value.length === parseInt(this.attributes["maxlength"].value)) {
$('#num_3').focus();
}
})
<input type="text" class="form-control" name="number" maxlength="3" id="num_1">
<input type="text" class="form-control" name="number" maxlength="3" id="num_2">
<input type="text" class="form-control" name="number" maxlength="4" id="num_3">
If you're focused on creating card(debit/credit) number input type. Then clean an easily manageable jQuery version as follows:
/*..............................................................................................
* jQuery function for Credit card number input group
......................................................................................................*/
// make container label of input groups, responsible
$('.card-box').on('focus', function(e){
$(this).parent().addClass('focus-form-control');
});
$('.card-box').on('blur', function(e){
$(this).parent().removeClass('focus-form-control');
});
$('.card-box-1').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-2').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
return;
}
});
$('.card-box-2').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-3').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
$('.card-box-1').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
$('.card-box-3').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
$('.card-box-4').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
if(_length <= 0){
$('.card-box-2').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
$('.card-box-4').on('keyup', function(e){
e.preventDefault();
var max_length = parseInt($(this).attr('maxLength'));
var _length = parseInt($(this).val().length);
if(_length >= max_length) {
return;
}
if(_length <= 0){
$('.card-box-3').focus().removeAttr('readonly');
$(this).attr('readonly', 'readonly');
}
});
/*..............................................................................................
* End jQuery function for Credit card number input group
......................................................................................................*/
/* Hide HTML5 Up and Down arrows. */
input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none; margin: 0;
}
input[type="number"] { -moz-appearance: textfield; }
.card-box {
width: 20%; display: inline-block; height: 100%; border: none;
}
.focus-form-control {
border-color: #66afe9; outline: 0;-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="form-control" style="padding: 0; max-width: 300px; ">
<input class="card-box card-box-1" type="number" id="CreditCard_CardNumber1" required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;"/>
<input class="card-box card-box-2" type="number" id="CreditCard_CardNumber2" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
<input class="card-box card-box-3" type="number" id="CreditCard_CardNumber3" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
<input class="card-box card-box-4" type="number" id="CreditCard_CardNumber4" readonly required step="1" minlength="4" maxlength="4" pattern="[0-9]{4}" value="" placeholder="0000"
onClick="this.setSelectionRange(0, this.value.length)" oninput="this.value=this.value.slice(0,this.maxLength||'');this.value=(this.value < 1) ? ('') : this.value;" />
</div>
Verified Answer have one issue which focus previous field if previous field have valid length
I have Modified Above Answer to fix complete length of previous tag
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;
// Move to previous field if backspace is pressed
if (code == 8) {
previous = previous.previousElementSibling;
if (previous != null) {
if (previous.tagName.toLowerCase() === "input") {
previous.focus();
}
}
} else {
while (previous = previous.previousElementSibling) {
if (previous == null)
break;
if (previous.tagName.toLowerCase() === "input") {
var mLength = parseInt(previous.attributes["maxlength"].value, 10);
var pMyLength = previous.value.length;
// Move to previous field if it does not have required length
if (mLength == pMyLength) {
break;
} else {
previous.focus();
break;
}
}
}
}
}
}
I think this is a shorter way. As long as you use a specific structure in HTML
const inputHandler = (event) => {
if (event.target.nodeName == "INPUT" && event.target.nextElementSibling != null) {
event.target.nextElementSibling.focus();
}
}
<div class="enter-code">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
<input type="text" maxlength="1" #keyup="inputHandler">
</div>