js 正则表达式让input框只能输入小数点后三位?

有一个如下的正则表达式,我想用于保证输入框内输入的数字只能输入到小数点后三位。
但是用了这个正则表达式一输入小数点,输入的光标就回到最前面去了也没输入小数点
输入框是type为number的input框,正则表达式相关代码如下

const inputCheck = e => {
            
            e.target.value = e.target.value.replace(/(\.\d{0,3})\d*/,'$1')
          }
          document.querySelectorAll('input').forEach(item => {
            item.addEventListener('input', e => {inputCheck(e)})
          })

希望能实现保证小数点后只能最多输入三位的效果。。

/这里往下时追加问题
后面了解到很可能是type为number的input框做了什么操作影响结果,于是直接采用原生input并加上正则进行规范。代码如下:

const inputCheck = e => {
            e.target.value = e.target.value.replace(/[^\d.-]/, '')
            e.target.value = e.target.value.replace(/\.+/, '.')
            e.target.value = e.target.value.replace(
              /(\d)(\.\d{0,3})\d*/,
              '$1$2'
            )
            e.stopPropagation()
          }

          //todo:セレクトのinputに影響を及ぼすかも?
          document.querySelectorAll('input').forEach(item => {
            item.addEventListener('input', e => {
              e.target.value = e.target.value.replace(/^0/, '')
              inputCheck(e)
            })
          })
          document.querySelectorAll('input').forEach(item => {
            item.addEventListener('blur', e => {
              inputCheck(e)

            })
          })
          document.querySelectorAll('input').forEach(item => {
            item.addEventListener('focus', e => {
              inputCheck(e)
            })
          })

以上的代码是已经好使的代码。
此处的问题是:如果不加上对于blur和focus进行监听并清除,会出现比较诡异的一失去焦点就出现之前的最后一个数字的情况,也就是如果只写上

const inputCheck = e => {
            e.target.value = e.target.value.replace(/[^\d.-]/, '')
            e.target.value = e.target.value.replace(/\.+/, '.')
            e.target.value = e.target.value.replace(
              /(\d)(\.\d{0,3})\d*/,
              '$1$2'
            )
            e.stopPropagation()
          }

          //todo:セレクトのinputに影響を及ぼすかも?
          document.querySelectorAll('input').forEach(item => {
            item.addEventListener('input', e => {
              e.target.value = e.target.value.replace(/^0/, '')
              inputCheck(e)
            })
          })

只写这一段的话,代码会有bug。必须加上blur和foces才能完全好使(这是我试出来的),但我不知道原理,哪位哥可以讲一下原理吗?为什么blur和foces触发的时候会影响input的值

阅读 1k
5 个回答

Demo
你这个正则其实没问题,有问题的是e.target.value这里赋值

// 此时input框内是"123."
// 而e.target.value = 123,小数点被省略掉了
// 123值不符合该正则的条件,所以replace未执行
e.target.value = e.target.value.replace(/(\.\d{0,3})\d*$/,function(match){
  // 这句并未被执行
  console.log(111)
  return '$1'
})
// 等同于
e.target.value = e.target.value
// 因为e.target.value在这里被重新赋值,所以input的内容123.也被更新为了e.target.value的值123

所以改成下面这样就行了

if(/\./.test(e.target.value)){
    e.target.value = e.target.value.replace(/(\.\d{0,3})\d*$/,'$1')  
}
// 正则表达式,允许整数和小数点后最多三位数字
 var regex = /^\d+(\.\d{1,3})?$/;

最好写一个自定义指令,用指令去控制,给你分享一个我现在用的指令,可以限制正负浮点数位数、正负整数。

/**
 * @desc 限制输入框只输入数字----此指令主要是为了适用可以输入负整数和负浮点数
 * @example
 * 最大两位正小数: <el-input v-number-input:true.float="2" v-model="price"/>
 * 最大两位负小数: <el-input v-number-input.float="2" v-model="price"/>
 * 只允许输入整数: <input v-number-input v-model="price"/>
 * 默认正整数 usedMinus为true时可以输入负数,false不允许输入负数
 * <el-input v-number-input:[usedMinus] v-model="price"/>
 */
export default {
  inserted(el, binding, vnode) {
    const input =
      el.tagName === "INPUT" ? el : vnode.elm.querySelectorAll("input")[0];
    input.addEventListener("compositionstart", () => {
      // eslint-disable-next-line no-param-reassign
      vnode.inputLocking = true;
    });
    input.addEventListener("compositionend", () => {
      // eslint-disable-next-line no-param-reassign
      vnode.inputLocking = false;
      input.dispatchEvent(new Event("input"));
    });
    input.addEventListener("input", () => {
      if (vnode.inputLocking) {
        return;
      }
      const usedMinus = binding.arg || false;
      const oldValue = input.value;
      let newValue = input.value;
      // modifiers为修饰符对象,传入了float,则其float属性为true
      if (binding.modifiers.float) {
        // 负数时候
        // 清除"数字"和"."以外的字符
        if (usedMinus) {
          newValue = newValue.replace(/[^\d.-]/g, "");
        } else {
          newValue = newValue.replace(/[^\d.]/g, "");
        }
        newValue = newValue.replace(/[^\d.-]/g, "");
        // 只保留第一个-, 清除多余的
        newValue = newValue.replace(/-{2,}/g, "-");
        newValue = newValue.replace(/\.{2,}/g, ".");
        // 第一个字符如果是.号,则补充前缀0
        newValue = newValue.replace(/^\./g, "0.");
        // 前两个字符如果是 -. 则补充前面的0
        newValue = newValue.replace(/^-\./g, "-0.");
        // 0开头的只保留第一个0, 清除多余的
        newValue = newValue.replace(/^0{2,}/g, "0");
        // -0开头的只保留第一个0, 清除多余的0
        newValue = newValue.replace(/^-0{2,}/g, "-0");
        // 两位数已上不能0开头
        if (/^0\d+/.test(newValue)) {
          newValue = newValue.slice(1);
        }
        // 两位数已上不能0开头
        if (/^-0\d+/.test(newValue)) {
          newValue = `-${newValue.slice(2)}`;
        }
        // 保证.只出现一次,而不能出现两次以上
        newValue = newValue
          .replace(".", "$#$")
          .replace(/\./g, "")
          .replace("$#$", ".");
        // 保证-只出现一次,而不能出现两次以上
        newValue = newValue
          .replace(/^-/, "$#$")
          .replace(/-/g, "")
          .replace("$#$", "-");
        // 保留几位小数
        if (typeof binding.value !== "undefined") {
          // 期望保留的最大小数位数
          let pointKeep = 0;
          if (
            typeof binding.value === "string" ||
            typeof binding.value === "number"
          ) {
            pointKeep = parseInt(binding.value, 10);
          }
          if (!isNaN(pointKeep)) {
            if (!Number.isInteger(pointKeep) || pointKeep < 0) {
              pointKeep = 0;
            }
            const str = `^(-?)(\\d+)\\.(\\d{${pointKeep}}).*$`;
            const reg = new RegExp(str);
            if (pointKeep === 0) {
              // 不需要小数点
              newValue = newValue.replace(reg, "$1$2");
            } else {
              // 通过正则保留小数点后指定的位数
              newValue = newValue.replace(reg, "$1$2.$3");
            }
          }
        }
      } else {
        // 只保留整数
        if (usedMinus) {
          // newValue = newValue.replace(/[^\-?\d][^\d]/g, '');
          newValue = newValue.replace(/[^\d-]/g, ""); // 删除所有非数字和非负号字符
          if (newValue[0] !== "-") {
            // 如果第一个字符不是负号
            newValue = newValue.replace(/-/g, ""); // 删除所有负号
          } else {
            newValue = `-${newValue.slice(1).replace(/-/g, "")}`; // 保留第一个负号,删除其他负号
          }
        } else {
          newValue = newValue.replace(/[^\d]/g, "");
        }
        if (newValue !== "-") {
          newValue = newValue ? parseInt(newValue, 10) : "";
        }
      }
      // 判断是否需要更新,避免进入死循环
      if (newValue.toString() !== oldValue.toString()) {
        input.value = newValue;
        input.dispatchEvent(new Event("input"));
      }
    });

    // 验证是否是数值
    input.addEventListener("change", () => {
      const inpValue = input.value;
      if (isNaN(+inpValue) || /-\.$/.test(inpValue)) {
        input.value = "";
      }
      if (/^-0\.?$/.test(inpValue)) {
        input.value = 0;
      }
      if (/\.$/.test(inpValue)) {
        input.value = inpValue.slice(0, inpValue.toString().length - 1);
      }
    });
  },
};

限制只能输入三位小数:
<el-input v-number-input:true.float="3" v-model="price"/>

image.png
你没有考虑小数点前的数字,这段正则无法匹配只有整数的数字,就会导致整数部分被清除image.png

不是正则表达式的问题,是input的type设置的问题,改成type="text"就可以了,
第一个输入框type="text"就没问题,第二个输入框type="number"就有问题了,
然后可以通过下面的代码方法限制只能输入数字和小数点

const inputCheck = (e) => {
  // 允许输入数字和小数点
  const regex = /^(\d*\.?\d*)$/;
  let value = e.target.value;
  if (!regex.test(value)) {
    // 移除非法字符
    value = value.replace(/[^0-9.]/g, "");
    // 仅允许一个小数点
    const parts = value.split(".");
    if (parts.length > 1) {
      value = `${parts[0]}.${parts.slice(1).join("")}`;
    }
  }
  //只能输入到小数点后三位
  e.target.value = value.replace(/(\.\d{0,3})\d*/, "$1");
};

https://developer.mozilla.org/zh-CN/play?id=mwIG9b4Ai25phf4tJ...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏