如何通过忽略每次按键上的无效字符来使文本类型的输入元素只接受一个数值(十进制、正数和负数)?

Posted

技术标签:

【中文标题】如何通过忽略每次按键上的无效字符来使文本类型的输入元素只接受一个数值(十进制、正数和负数)?【英文标题】:How to make a text-type input-element accept just a numerical value (decimal, positive and negative) by ignoring invalid characters on each keypress? 【发布时间】:2021-11-25 13:26:51 【问题描述】:

我有一个 html 输入,这个输入只接受数字字符串,

例子:

输入值:+0123.534534或-234234.543345或-13453,这些输入值有效。 字符 + 或 - 仅存在于字符串值中的第一个位置

我希望每个输入字符的输入值都应该保留当前有效的字符串,并用空字符替换无效的输入字符

例子:

当我输入:+123.g ==> 值应立即替换+123。 或者当我输入:g ==> 值应立即替换为空值。

我找到了一个实现,但它缺少 (+/-/.) 字符

const getDigitsOnly = (value) => String(value).replace(NOT_NUMBERS, '');

【问题讨论】:

为什么 OP 不通过数字类型输入字段来限制用户输入,例如 ... <input type="number"/> 任何基于type="text" 和自定义验证的方法,如果做得好,都有变得更加复杂的趋势,因为从用户体验的角度来看,也必须采取即时值清理(在键入或粘贴时) care 重新建立用户最近的插入符号位置。 “当我输入:+123.g ==> 值应该立即替换+123 为什么是+123 而不是+123.?如果+123. 无效,您要立即将其替换为+123 还是需要单独处理的额外特殊情况? @nhlinh 是 .123 一个有效的输入吗? @nhlinh 请回答我之前的评论! 【参考方案1】:

这会做你想做的事

    let someString = "+123.g";
    let result = someString.replace(/[^0-9,+,-,.]+/g, "");
    console.log(result);

有关如何使用正则表达式的更多信息,请查看此处 https://developer.mozilla.org/en-US/docs/Web/javascript/Guide/Regular_Expressions

【讨论】:

我认为您误解了字符类[...] 的工作原理。无需在每个字符之间添加逗号。添加, 实际上意味着您将在输入中允许,。这也不允许使用句点字符 (.)。 我从帖子中了解到,它需要允许 + 和 - ,是的,如果需要,我会添加 (.) 。我会编辑。 根据我的帖子,(+/_) 只存在一个,并且只存在于字符串的第一个位置 @MicahelHamami 我认为您没有解决我的担忧?为什么要多次添加,?而,-, 将正常失败,因为- 现在具有特殊含义,因为它不在字符类的开头或结尾。【参考方案2】:

这是我的解决方案。

我们向输入框添加一个input 事件侦听器,并且对于每个输入,我们都会格式化整个输入值。

注意:提交值时不要忘记将尾随小数点(例如"123." 改为"123")。

const inputBox = document.getElementById("inputBox");

inputBox.addEventListener("input", () => 
  inputBox.value = format(inputBox.value);
);

function format(str) 
  if (!str.length) return str;

  str = str.replace(/[^.+-\d]/g, "");
  const firstChar = str.charAt(0);
  str = str.slice(1).replace(/[+-]/g, "");

  // Bug fix. Credit goes to @Peter Seliger
  // For pointing out the bug
  const charArray = [];
  let hasDecimalPoint = false;

  if (firstChar === ".") 
    if (!str.includes(".")) 
      hasDecimalPoint = true;
      charArray.push("0.");
    
   else charArray.push(firstChar);
  // End bug fix

  for (let i = 0; i < str.length; i++) 
    const char = str.charAt(i);

    if (char === ".") 
      if (hasDecimalPoint) continue;
      if (!i && firstChar !== "0") charArray.push("0");
      hasDecimalPoint = true;
    

    charArray.push(char);
  

  return charArray.join("");
<p>Enter your number here</p>
<input id="inputBox" type="text" />

格式化函数的算法。

1:如果输入为空,则返回输入 2:删除除“-”、“+”、“.”以外的所有字符和数字 3:将第一个字符存储在变量中 4:删除第一个字符后的所有“+”和“-”(如果存在) 5:如果第一个字符是“.”然后将其替换为“0”。 6. 最后删除所有重复的小数点(“.”)(如果存在)

这里有一些虚拟数据来测试格式功能

const dummyData = [
  "",
  "+",
  "-",
  ".",
  ".123",
  "+.123",
  "123.3.23",
  "12sfdlj3lfs.s++d_f",
  "12--++.123",
];

【讨论】:

@nhlinh 我认为您接受了错误的答案。 Micahel Hamami's 答案没有通过我在答案中提供的所有测试用例。请接受Peter Seliger's 或我的回答。根据您的评论.123(也可能是+.123-.121)是无效的。我的算法将通过添加 0. 来成功处理这些情况。 @PeterSeliger 这将是表单验证的一部分。不是问题中要求的工作。您可以简单地使用Number.isSafeInterger 或类似的方法进行检查。 等等让我看看。感谢您的反馈 @PeterSeliger 和nhlinh 我已经修复了这个错误。非常感谢彼得 不客气。【参考方案3】:

从上面的cmets ...

“为什么 OP 没有像 ...&lt;input type="number"/&gt; 这样的数字类型输入字段来限制用户输入?”

“任何基于type="text" 和自定义验证的方法,如果处理得当,都有变得更加复杂的趋势,因为从用户体验的角度来看,即时值清理(在键入或粘贴时)也必须小心重新建立用户最近的插入符号位置。”

证明上述复杂性...

function stripNeedlessDataFromBoundFieldValue() 
  this.value = this.value.replace((/[-+.]$/), '');


function getSanitizedValue(value) 
  value = value
    // remove any leading and trailng whitespace (sequences).
    .trim()
    // remove any character not equal to minus, plus, dot and digit.
    .replace((/[^-+.\d]+/g), '');

  if (value.length >= 1) 

    let partials = value.split(/(^[+-]?)/);
    if (partials.length === 1) 

      partials.unshift('');
     else 
      partials = partials.slice(1);
    
    let [ first, last ] = partials;

    last = last.replace((/[+-]+/g), '');

    // console.log([ first, last ]);

    partials = last.split('.');
    if (partials.length === 1) 

      partials.unshift('');
     else 
      partials = [
        partials.shift(),
        ['.', partials.join('')].join(''),
      ];
    
    first = [first, partials[0]].join('');
    last = partials[1];

    value = [first, last]
      .join('')
      .replace(
        // trim any sequence of leading zeros into a single one.
        (/(^[+-]?)0+/),
        (match, sign) => [(sign || ''), 0].join('')
      )
      .replace(
        // always ensure a single zero before a leading sole decimal point.
        (/(^[+-]?)\.+/),
        (match, sign) => [(sign || ''), '0.'].join('')
      );
  
  return value;


function sanitizeInputValue(evt) 
  const elmNode = evt.currentTarget;

  const currentValue = elmNode.value;
  const sanitizedValue = getSanitizedValue(currentValue);

  if (currentValue !== sanitizedValue) 
    const diff = sanitizedValue.length - currentValue.length;
    const  selectionStart, selectionEnd  = elmNode;

    elmNode.value = sanitizedValue;

    elmNode.selectionStart =
      (selectionStart + diff > 0) ? selectionStart + diff : selectionStart;
    elmNode.selectionEnd =
      (selectionEnd + diff > 0) ? selectionEnd + diff : selectionEnd;
  


function main() 
  const textInput = document.querySelector('[type="text"]');

  const finalizeBoundFieldValueDebounced = _.debounce(
    stripNeedlessDataFromBoundFieldValue.bind(textInput), 1200
  );
  textInput.addEventListener('input', evt => 
    sanitizeInputValue(evt);
    finalizeBoundFieldValueDebounced();
  );

  // // whithout delayed trimming of trailing dot.
  //
  // document
  //   .querySelector('[type="text"]')
  //   .addEventListener('input', sanitizeInputValue);


main();
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-umd-min.js"></script>

<input type="text" placeholder="... number only ..."/>

...并将其直接与数字类型字段进行比较...

&lt;input type="number" placeholder="native number type"/&gt;

...以及Micahel Hamami的方法,特此付诸实施...

function sanitizeInputValue( currentTarget ) 
  currentTarget.value = currentTarget.value.replace(/[^0-9,+,-,.]+/g, "");

function main() 
  document
    .querySelector('[type="text"]')
    .addEventListener('input', sanitizeInputValue);

main();
&lt;input type="text" placeholder="... number only ..."/&gt;

【讨论】:

@nhlinh ... 查看上面基于 Micahel Hamami 方法的可执行 sn-p 并将其与例如h-sifat 之一。 Micahel 的方法肯定不能满足您的要求。它已经失败了,例如防止+ 字符的序列。在编辑需要清理的长数字时,会丢失插入符号的位置。 我都检查过了。感谢您的提前 @nhlinh 我已经更新了我的解决方案。检查最新答案。非常感谢彼得

以上是关于如何通过忽略每次按键上的无效字符来使文本类型的输入元素只接受一个数值(十进制、正数和负数)?的主要内容,如果未能解决你的问题,请参考以下文章

javascript:在用户键入或更改文本框值时忽略无效输入[重复]

如何使用 ASP 文本框的 Textchange 属性

TextInput 中的每次按键后键盘都会关闭本机反应

如何在 Google Sheet 上的文本字符串中找到完全匹配并忽略第二个数字?

libGDX:如何在按键后逐行读取和显示.txt文件中的文本?

键盘上的每个键都各有啥作用?