达到 maxlength 值后关注下一个输入
Posted
技术标签:
【中文标题】达到 maxlength 值后关注下一个输入【英文标题】:Focus next input once reaching maxlength value 【发布时间】:2013-03-13 19:17:13 【问题描述】:前一个输入达到其最大长度值后,如何聚焦下一个输入?
a: <input type="text" maxlength="5" />
b: <input type="text" maxlength="5" />
c: <input type="text" maxlength="5" />
如果用户粘贴的文本大于最大长度,理想情况下它应该溢出到下一个输入中。
jsFiddle: http://jsfiddle.net/4m5fg/1/
我必须强调我不想使用插件,因为我更愿意学习其背后的逻辑,而不是使用已经存在的东西。感谢理解。
【问题讨论】:
@Musa 他的目的是“学习这背后的逻辑”。 Moving a focus when the input text field reaches a max length的可能重复 【参考方案1】:没有使用 jQuery,并且是一个非常干净的实现:
从 maxlength 属性读取。 可扩展到容器内任意数量的输入。 自动查找下一个要关注的输入。 没有 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;
【讨论】:
这仅适用于页面中的第一个容器。修改它以使其适用于所有这些是相对微不足道的,但值得指出的是,以防新手使用它。var target = e.srcElement || e.target;
帮助我使用了您的解决方案。谢谢!
如果添加 maxlength = 2 则无法编辑输入字段。
我必须将 maxLength
更改为 target.maxLength
而不是 parseInt(target.attributes["maxlength"].value, 10)
才能使其正常工作。但是,我使用的是正确的侦听器,而不是重新定义 onkeyup
函数:container.addEventListener('keyup', moveToNext)
(其中 moveToNext
是上述答案中的函数)。【参考方案2】:
您可以观察字段中的输入并测试其值:
$("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.
setTimeout
用于确保代码仅在输入完成并更新值后运行。绑定input
确保大多数类型的输入都会触发事件,包括按键、复制/粘贴(甚至从鼠标)和拖放(尽管在这种情况下,后者不起作用,因为焦点在可拖动,而不是可放置的)。
注意:在一些较旧的浏览器上,您可能还需要绑定propertychange
。
如果用户粘贴的文本大于最大长度,理想情况下它应该溢出到下一个输入中。
为此,您可能需要使用 javascript 删除 maxlength
属性(以便能够捕获完整的输入),然后自己实现该功能。我发了small example,相关部分如下:
$("input").each(function()
var $this = $(this);
$(this).data("maxlength", $this.prop("maxlength"));
$(this).removeAttr("maxlength");
)
这会删除该属性,但会将其保存在data
中,以便您稍后访问。
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);
这里在 JavaScript 中重新引入了最大长度逻辑,以及获取“丢弃”部分并在对 spill
的递归调用中使用它。如果没有下一个元素,对data
的调用将返回undefined
并且循环将停止,因此输入将在最后一个字段中被截断。
【讨论】:
您的小提琴完美无缺,为此 +1,但是绑定propertyChange
是强制性的吗?即使没有它,它似乎也能正常工作
@Bingo 根据this answer,input
适用于 Firefox,propertychange
适用于其他浏览器,但从那时起情况可能发生了变化(即,现在可能每个浏览器都支持 input
)。唯一可以确定的方法是在不同的环境中进行测试。 (附注:这是propertychange
,而不是propertyChange
- 我已经更正了我的答案)
这是一个不错的选择,但有问题,例如,如果您将每个输入放在不同的 div 中不起作用
没错,但是代码需要一种方法来指定“下一个输入”是什么——您不能只搜索页面中的任何输入并使用它,因为这种行为可能会导致问题... 整体代码很好,只是选择部分(在本例中为 .next
调用)需要适应每个特定情况(例如 .nextAll
如果它们都在同一个 div 中,或者.parent().next().find
如果它们都在自己的 div 中,等等)。
绑定已弃用,继续使用【参考方案3】:
您可以使用纯 JavaScript:
见DEMO。
使用el.value.length
检查字符长度。如果等于最大值,则使用focus()
移动到下一个字段。将此函数与onkeyup
绑定到keyup 事件,以便在用户每次键入字符后触发该函数。
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();
【讨论】:
使用 keyup 功能可能会产生问题,例如您必须在文本框中删除一些数字,该事件将不允许删除,因为它是按键。而是使用 oninput 事件。会有帮助的【参考方案4】:如果你有很多字段,你可以这样做。
基本上在keyup
上获取输入的长度,然后将其与最大长度进行比较,如果匹配,则将focus
放到下一个输入字段中。
http://jsfiddle.net/btevfik/DVxDA/
$(document).ready(function()
$('input').keyup(function()
if(this.value.length==$(this).attr("maxlength"))
$(this).next().focus();
);
);
【讨论】:
更新您的代码以获得更好的解决方案 - jsfiddle.net/ssuryar/DVxDA/224 - DEMO【参考方案5】:更新了 btevfik 代码,Onkeyup 或 onkeydown 将产生问题,因为您将无法删除标签导航上的先前输入。编辑或更改输入框中的文本将很困难,因为它将被限制为最大长度。所以我们可以使用oninput事件来完成任务。
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;
【讨论】:
这对我很有用,我还对其进行了修改,因此当您从焦点字段中删除所有内容时,它将转到上一个字段。 @icortesi,你可以分享这个修改,它对其他人非常有用。【参考方案6】: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>
【讨论】:
这只适用于最大长度为 1。问题显然不是假设这样的最大长度。【参考方案7】:其他答案确实给出了一些如何实现的想法,但我发现他们没有考虑一些小问题,其中包括:
-
事实上,您不希望在整个页面中自动聚焦任何元素,而是在特定表单中自动聚焦。
输入元素可以包装在其他一些元素中(例如,我将它们包装在
span
或 div
中以允许通过 CSS 浮动标签,并且我已经看到使用 table
的表单作为结构)。
字段的有效性,当溢出或自动移动到下一个时。
溢出时的输入事件。
返回前一个字段时的光标位置(看起来可以通过浏览器保存,因此退格可以不集中在字段的末尾,而是例如在中间)。
下面的代码至少试图解释所有这些。其中大部分可以在codepen 上进行测试:粘贴溢出在那里不起作用,看起来是因为剪贴板 API(其他带有它的代码笔也不适合我)。 如果代码中有任何不清楚的地方,请告诉我,我会更新我的答案和代码。如果您发现一些未涵盖的边缘情况 - 也请告诉我。
对于使用来自 codepen 的表单进行粘贴溢出测试,您可以使用如下内容:123456789123456789012345678903454353434534
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;
【讨论】:
【参考方案8】:如果您要动态添加输入文本字段,那么您可以试试这个。
这会将脚本重新注入 DOM 并完美运行。
$('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">
【讨论】:
【参考方案9】:如果您专注于创建卡(借记/贷记)号码输入类型。然后清理一个易于管理的 jQuery 版本,如下所示:
/*..............................................................................................
* 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>
【讨论】:
【参考方案10】:经过验证的答案有一个问题,如果前一个字段具有有效长度,则关注前一个字段
我已修改上述答案以修复前一个标签的完整长度
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;
【讨论】:
以上是关于达到 maxlength 值后关注下一个输入的主要内容,如果未能解决你的问题,请参考以下文章
android EditText maxLength 允许用户输入更多
在 MySQL 中选择 MIN 值后选择 MIN (MAX)