只允许输入数字的HTML文本输入

1109

有没有一种快速的方法可以设置HTML文本输入框(<input type=text />)只允许数字按键(包括 '.')?


102
这里的许多解决方案仅适用于按键输入。如果人们使用菜单粘贴文本,或将文本拖放到文本输入框中,则这些解决方案会失败。我以前就遇到过这种问题。请小心! - Bennett McElwee
103
@JuliusA - 无论如何你始终需要服务器端验证。 - Stephen P
73
<input type="text" onkeypress='return event.charCode >= 48 && event.charCode <= 57'></input> - yurisich
19
请注意,这也会禁用任何其他键,比如TAB键跳转到下一个输入框或者其他与输入无关的快捷键,如当输入框处于聚焦状态时,cmd+R用于刷新网页。 - Alejandro Pérez
1
onkeyup="this.value = this.value.replace(/[^0-9]/g, '');" - ribamar
显示剩余12条评论
80个回答

1356

JavaScript

您可以使用以下setInputFilter函数过滤文本<input>的输入值(支持复制+粘贴、拖放、键盘快捷方式、上下文菜单操作、不可输入键、插入符位置、不同键盘布局、有效性错误消息和IE 9及所有浏览器)。

// Restricts input for the given textbox to the given inputFilter function.
function setInputFilter(textbox, inputFilter, errMsg) {
  [ "input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop", "focusout" ].forEach(function(event) {
    textbox.addEventListener(event, function(e) {
      if (inputFilter(this.value)) {
        // Accepted value.
        if ([ "keydown", "mousedown", "focusout" ].indexOf(e.type) >= 0){
          this.classList.remove("input-error");
          this.setCustomValidity("");
        }

        this.oldValue = this.value;
        this.oldSelectionStart = this.selectionStart;
        this.oldSelectionEnd = this.selectionEnd;
      }
      else if (this.hasOwnProperty("oldValue")) {
        // Rejected value: restore the previous one.
        this.classList.add("input-error");
        this.setCustomValidity(errMsg);
        this.reportValidity();
        this.value = this.oldValue;
        this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
      }
      else {
        // Rejected value: nothing to restore.
        this.value = "";
      }
    });
  });
}

您现在可以使用setInputFilter函数安装输入过滤器:
setInputFilter(document.getElementById("myTextBox"), function(value) {
  return /^\d*\.?\d*$/.test(value); // Allow digits and '.' only, using a RegExp.
}, "Only digits and '.' are allowed");

将您喜欢的样式应用于input-error类。这是一个建议:

.input-error{
  outline: 1px solid red;
}

请注意,您仍然必须进行服务器端验证!另一个警告是它会破坏撤销堆栈,因为它直接设置this.value。这意味着在输入无效字符后,CtrlZ将无法撤消输入。

演示

查看JSFiddle演示以获取更多输入过滤器示例或运行下面的堆栈片段:

// Restricts input for the given textbox to the given inputFilter.
function setInputFilter(textbox, inputFilter, errMsg) {
  [ "input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop", "focusout" ].forEach(function(event) {
    textbox.addEventListener(event, function(e) {
      if (inputFilter(this.value)) {
        // Accepted value.
        if ([ "keydown", "mousedown", "focusout" ].indexOf(e.type) >= 0) {
          this.classList.remove("input-error");
          this.setCustomValidity("");
        }
        
        this.oldValue = this.value;
        this.oldSelectionStart = this.selectionStart;
        this.oldSelectionEnd = this.selectionEnd;
      }
      else if (this.hasOwnProperty("oldValue")) {
        // Rejected value: restore the previous one.
        this.classList.add("input-error");
        this.setCustomValidity(errMsg);
        this.reportValidity();
        this.value = this.oldValue;
        this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
      }
      else {
        // Rejected value: nothing to restore.
        this.value = "";
      }
    });
  });
}

// Install input filters.
setInputFilter(document.getElementById("intTextBox"), function(value) {
  return /^-?\d*$/.test(value);
}, "Must be an integer");
setInputFilter(document.getElementById("uintTextBox"), function(value) {
  return /^\d*$/.test(value);
}, "Must be an unsigned integer");
setInputFilter(document.getElementById("intLimitTextBox"), function(value) {
  return /^\d*$/.test(value) && (value === "" || parseInt(value) <= 500);
}, "Must be between 0 and 500");
setInputFilter(document.getElementById("floatTextBox"), function(value) {
  return /^-?\d*[.,]?\d*$/.test(value);
}, "Must be a floating (real) number");
setInputFilter(document.getElementById("currencyTextBox"), function(value) {
  return /^-?\d*[.,]?\d{0,2}$/.test(value);
}, "Must be a currency value");
setInputFilter(document.getElementById("latinTextBox"), function(value) {
  return /^[a-z]*$/i.test(value);
}, "Must use alphabetic latin characters");
setInputFilter(document.getElementById("hexTextBox"), function(value) {
  return /^[0-9a-f]*$/i.test(value);
}, "Must use hexadecimal characters");
.input-error {
  outline: 1px solid red;
}
<h2>JavaScript input filter showcase</h2>
<p>Supports Copy+Paste, Drag+Drop, keyboard shortcuts, context menu operations, non-typeable keys, the caret position, different keyboard layouts, and <a href="https://caniuse.com/#feat=input-event" target="_blank">all browsers since IE 9</a>.</p>
<p>There is also a <a href="https://jsfiddle.net/emkey08/tvx5e7q3" target="_blank">jQuery version</a> of this.</p>
<table>
  <tr>
    <td>Integer</td>
    <td><input id="intTextBox"></td>
  </tr>
  <tr>
    <td>Integer &gt;= 0</td>
    <td><input id="uintTextBox"></td>
  </tr>
  <tr>
    <td>Integer &gt;= 0 and &lt;= 500</td>
    <td><input id="intLimitTextBox"></td>
  </tr>
  <tr>
    <td>Float (use . or , as decimal separator)</td>
    <td><input id="floatTextBox"></td>
  </tr>
  <tr>
    <td>Currency (at most two decimal places)</td>
    <td><input id="currencyTextBox"></td>
  </tr>
  <tr>
    <td>A-Z only</td>
    <td><input id="latinTextBox"></td>
  </tr>
  <tr>
    <td>Hexadecimal</td>
    <td><input id="hexTextBox"></td>
  </tr>
</table>

TypeScript

这是一个 TypeScript 版本。

function setInputFilter(textbox: Element, inputFilter: (value: string) => boolean, errMsg: string): void {
  ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop", "focusout" ].forEach(function(event) {
    textbox.addEventListener(event, function(this: (HTMLInputElement | HTMLTextAreaElement) & { oldValue: string; oldSelectionStart: number | null, oldSelectionEnd: number | null }) {
      if (inputFilter(this.value)) {
        this.oldValue = this.value;
        this.oldSelectionStart = this.selectionStart;
        this.oldSelectionEnd = this.selectionEnd;
      }
      else if (Object.prototype.hasOwnProperty.call(this, "oldValue")) {
        this.value = this.oldValue;
        
        if (this.oldSelectionStart !== null &&
          this.oldSelectionEnd !== null) {
          this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
        }
      }
      else {
        this.value = "";
      }
    });
  });
}

jQuery

这里也有一个 jQuery 版本,可以参考 这个答案

HTML5

HTML5 有一个本地的解决方案,可以使用 <input type="number">(请参阅规范文档)。文档中还有此输入类型的工作演示。

  • 不要读取 value 属性,应该读取输入的 valueAsNumber 属性,以获取输入的值作为数字而不是字符串。
  • 推荐在<form> 中使用,因为这样可以更容易进行验证;例如,如果值无效,则按下 Enter 将自动显示错误消息。
    • 您可以使用整个表单上的 checkValidity 方法或 requestSubmit 方法来明确检查有效性。
    • 请注意,您可能需要使用 required 属性来禁止空输入。
  • 您可以在输入元素本身上使用 checkValidity 方法或 validity 属性来明确检查有效性。
  • 您可以使用 reportValidity 显示错误消息,并使用 setCustomValidity 设置自己的消息。

此方法基本上具有不同的用户体验:您允许输入无效字符,验证是分开执行的。 这样做的好处是撤消堆栈(CtrlZ)不会被打破。 请注意,无论选择哪种方法,都必须执行服务器端验证。

但请注意,浏览器支持各不相同:

演示

document.querySelector("form").addEventListener("submit", (event) => {
  event.preventDefault();
  console.log(`Submit!
  Number is ${event.target.elements.number.valueAsNumber},
  integer is ${event.target.elements.integer.valueAsNumber},
  form data is ${JSON.stringify(Object.fromEntries(new FormData(event.target).entries()))}.`);
})
label {
  display: block;
}
<form>
  <fieldset>
    <legend>Get a feel for the UX here:</legend>
    <label>Enter any number: <input name="number" type="number" step="any" required></label>
    <label>Enter any integer: <input name="integer" type="number" step="1" required></label>
    <label>Submit: <input name="submitter" type="submit"></label>
  </fieldset>
</form>


7
仍然不受Firefox 21的支持(我甚至不说IE9或更早版本...) - JBE
12
type="number" 并不能防止在输入字段中输入无效的文本;似乎即使在 Chrome 中,您也可以剪切和粘贴垃圾数据到该字段中。 - perfectionist
1
这也允许输入“e”值,我想是用于科学计数法中的数字10e3。 - Diego Quirós
如果您想要输入浮点数,请在“输入美元”处输入<input ng-model="dollar" placeholder="请输入美元" onkeypress='return event.charCode >= 48 && event.charCode <= 57 || event.charCode == 46'>。 - Shahzain ali
1
为什么仅使用input事件是不够的? - Alex78191
显示剩余13条评论

327

使用这个DOM

<input type='text' onkeypress='validate(event)' />

并且这个脚本

function validate(evt) {
  var theEvent = evt || window.event;

  // Handle paste
  if (theEvent.type === 'paste') {
      key = event.clipboardData.getData('text/plain');
  } else {
  // Handle key press
      var key = theEvent.keyCode || theEvent.which;
      key = String.fromCharCode(key);
  }
  var regex = /[0-9]|\./;
  if( !regex.test(key) ) {
    theEvent.returnValue = false;
    if(theEvent.preventDefault) theEvent.preventDefault();
  }
}

12
在EeePC 900上进行德语设置时,一些关键的可用性键无法正常工作,包括退格键(keyCode:8)和导航键左右(keyCode:37、38)。同时也可以进行复制和粘贴操作。 - Michael Piendl
11
大多数人确实在意,脚本错误的出现会给你的网站带来不好的反应。 - Robert Jeppesen
14
这段代码有一些问题。首先,你可以输入“.”不止一次。其次,它不允许使用删除键。有什么解决方法吗? - coure2011
4
我关心的是退格键、删除键和箭头键不能正常工作。如果您移除“theEvent.keycode ||”,并添加:“if( /[ -~]/ && !regex.test(key) ) { }”,则它会更好地工作(对于ASCII / UTF而言)。但那样就不能拒绝中文字符了! :) - Sam Watkins
4
这可以让任何人将随机内容粘贴到输入框中。 - Vitim.us
显示剩余19条评论

259

这是一个简单的解决方案,允许输入一个小数,但只限一个。输入事件使用正则表达式根据以下两个模式实时替换文本:

  1. 删除任何不是数字或点的字符
  2. 删除第二个点的实例

<input type="text" oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*?)\..*/g, '$1');" />

正如下面的评论所述,上面的解决方案不能处理前导零。如果您的特定用例要求不允许这样做,您可以像以下那样添加模式:

<input type="text" oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*?)\..*/g, '$1').replace(/^0[^.]/, '0');" />

这将允许0.123.123,但不允许012300.123


34
句柄:复制+粘贴,拖放,只允许1个小数,制表符,删除,退格-使用此选项。 - Mathemats
1
虽然它确实做到了@Mathemats所说的一切,但它无法处理带有多个小数点的复制+粘贴。所有粘贴文本的小数位都会被相加。 - Mr. Simmons
2
@Mr.Simmons - 很好的发现,谢谢指出。我做了一个小调整,应该能解决那种情况。 - But those new buttons though..
5
这是最好的解决方案。我几年前就点赞了它,但每次我搜索这个问题的解决方案时,我都知道这个答案在某个地方,并且每次都会回到它。我不知道 OP 在施展什么样的魔法,但即使我通过检查器删除事件,规则仍然适用于输入。 - Ethan Moore
2
@FiddlingAway - 如果你有一个"123.45"的数字,然后你把光标移动到要插入一个点的位置,变成了"1.23.45",那么应该保留哪个点呢?我的方法是简单地清除新输入点之后的所有内容。当然还有其他方法,但由于输入是无意义的,所以这种方法对我来说似乎是合理的。 - But those new buttons though..
显示剩余15条评论

173

我为此进行了长时间的搜索,但我们迫切需要<input type="number",但除此之外,这是我能想到的最简洁的两种方法:

<input type="text" 
           onkeyup="this.value=this.value.replace(/[^\d]/,'')">
如果您不喜欢在被删除之前短暂地显示非接受字符,则下面的方法是我的解决方案。请注意许多其他条件,这是为了避免禁用各种导航和快捷键。如果有人知道如何压缩此内容,请告诉我们!
<input type="text" 
onkeydown="return ( event.ctrlKey || event.altKey 
                    || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) 
                    || (95<event.keyCode && event.keyCode<106)
                    || (event.keyCode==8) || (event.keyCode==9) 
                    || (event.keyCode>34 && event.keyCode<40) 
                    || (event.keyCode==46) )">

5
HTML 5中引入了"type=number"输入类型,你可以使用JavaScript作为回退填充器。详情请见:http://www.stevefenton.co.uk/Content/Blog/Date/201105/Blog/HTML-5-Forms-Number-Input-Elements/ - Fenton
5
好的方法,但是长按一个不被接受的键就会失效。 - user527892
8
将正则表达式更改为/[^\d+]/,就可以在按住键时使用。 - boecko
12
谢谢@boecko提供的内容,但请注意应该使用/[^\d]+/。虽然很好的解决方案。还有@user235859。 - Mosselman
11
他希望也允许 . 。你应该将它实际写成 /[^0-9.]/g - Qsario
显示剩余8条评论

68

大多数回答都有一个缺点,那就是使用键盘事件。

许多答案会限制您使用键盘宏、复制+粘贴等操作,产生不必要的行为。另外一些答案似乎依赖于特定的jQuery插件,这就像用重型武器打蚊子。

以下简单的解决方案对我来说跨平台效果最佳,无论输入方式(按键、复制+粘贴、右键复制+粘贴、语音转文字等),所有文本选择键盘宏仍然可以正常使用,并且甚至可以限制通过脚本设置非数字值的能力。

function forceNumeric(){
    var $input = $(this);
    $input.val($input.val().replace(/[^\d]+/g,''));
}
$('body').on('propertychange input', 'input[type="number"]', forceNumeric);

需要jQuery 1.7+版本。这是一个更完整的答案,因为它考虑了通过“复制”输入的情况。而且也更简单! - Memochipan
另一种正则表达式:replace(/[^\d]+/g,'') 用空字符串替换所有非数字字符。不需要使用“i”(大小写不敏感)修饰符。 - Memochipan
这应该放在顶部,因为标签中的“onkey”事件处理程序不应再被传播... - gpinkas
8
如果您想要小数,您可以更改正则表达式/[^\d,.]+/ - EJTH
@EJTH - 查询 input[type="number"] 永远无法找到 input[type="text"] 元素。这就是问题所在。请问自己:如果您将 input[type="number"] 替换为 input[type="text"],这个 polyfill 是否仍然有效(并且不会干扰可能存在的其他文本输入)? - JPA
显示剩余11条评论

59

HTML5 支持正则表达式,因此您可以使用以下内容:

<input id="numbersOnly" pattern="[0-9.]+" type="text">

警告:部分浏览器目前还不支持此功能。


8
HTML5也有<input type=number> - Mathias Bynens
1
真的。请将此作为答案。哎呀,@Ms2ger已经回答了。 - james.garriss
65
仅在提交时检查模式。您仍然可以输入字母。 - Erwin
在模式属性中,+代表什么意思? - Sanjay Goswami
“+”表示重复,意思是“一次或多次”。请参阅http://www.regular-expressions.info/repeat.html。 - james.garriss
显示剩余2条评论

59

HTML5引入了<input type=number>,这对你来说听起来很合适。目前只有Opera原生支持它,但是有一个项目提供了JavaScript实现。


这里有一份文档,定义了哪些浏览器支持这个属性:http://caniuse.com/input-number。在撰写本文时,Chrome和Safari都完全支持此类型字段。IE 10部分支持,Firefox不支持。 - Nathan Wallace
type=number 的唯一问题是它不被 IE9 支持。 - J Rod
@JRod 这里有一个针对旧版IE的polyfill,更多详细信息点击这里 - jkdev
14
第一个问题是 type=number 允许输入 e 字符,因为它将 e+10 视为数字。第二个问题是无法限制输入中的最大字符数。 - Hakan Fıstık
另一个问题是,如果类型是数字,那么我就无法享受将类型设置为“tel”等的好处。 - ScottyBlades

58

还有一个例子,对我来说非常有效:

function validateNumber(event) {
    var key = window.event ? event.keyCode : event.which;
    if (event.keyCode === 8 || event.keyCode === 46) {
        return true;
    } else if ( key < 48 || key > 57 ) {
        return false;
    } else {
        return true;
    }
};

同时绑定到键盘按下事件

$(document).ready(function(){
    $('[id^=edit]').keypress(validateNumber);
});

还有HTML:

<input type="input" id="edit1" value="0" size="5" maxlength="5" />

这是一个 jsFiddle 的例子


当我调用这个函数时,为什么会出现“事件未定义”的错误? - Vincent
你是否在jQuery的keypress事件中调用了validateNumber函数? - Vasyl
3
当使用Firefox浏览器时,如果事件的keyCode始终返回0,则此代码无效。我使用新代码进行修复:function validateNumber(event) { var key = event.which || event.charCode || event.keyCode || 0; if (key == 8 || key == 46 || key == 37 || key == 39) { return true; } else if ( key < 48 || key > 57 ) { return false; } return true; }; - Luan Nguyen
1
正如@Jessica所说,您可以添加撇号甚至百分号。 - alejnavab
1
非常接近!但是允许多个小数。 - kross
显示剩余7条评论

43
我选择采用这里提到的两个答案的结合,即:
<input type="number" />
function isNumberKey(evt){
    var charCode = (evt.which) ? evt.which : evt.keyCode
    return !(charCode > 31 && (charCode < 48 || charCode > 57));
}

<input type="text" onkeypress="return isNumberKey(event);">


优秀的回答... +1。您还可以将if语句简化为:return !(charCode > 31 && (charCode < 48 || charCode > 57)); - schadeck
7
删除怎么样?你需要数字,但可能希望人们能够在不刷新页面的情况下进行更正。 - Martin Erlic

22

JavaScript

function validateNumber(evt) {
    var e = evt || window.event;
    var key = e.keyCode || e.which;

    if (!e.shiftKey && !e.altKey && !e.ctrlKey &&
    // numbers   
    key >= 48 && key <= 57 ||
    // Numeric keypad
    key >= 96 && key <= 105 ||
    // Backspace and Tab and Enter
    key == 8 || key == 9 || key == 13 ||
    // Home and End
    key == 35 || key == 36 ||
    // left and right arrows
    key == 37 || key == 39 ||
    // Del and Ins
    key == 46 || key == 45) {
        // input is VALID
    }
    else {
        // input is INVALID
        e.returnValue = false;
        if (e.preventDefault) e.preventDefault();
    }
}

另外,您可以添加逗号、句号和破折号(,.-)。

  // comma, period and minus, . on keypad
  key == 190 || key == 188 || key == 109 || key == 110 ||

HTML

<input type="text" onkeydown="validateNumber(event);"/ >

在需要使用Shift键输入数字的键盘上(例如欧洲AZERTY键盘),功能无法正常运行。 - Captain Sensible
谢谢你,真的很懂这个问题!漂亮的例子,当然也包括处理逗号或小数点(如果大多数人使用数字时需要)。 - real_yggdrasil
这是唯一一个有效防止粘贴非数字值的方法。 - DEREK LEE
不允许以任何方式粘贴。 - andreszs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接