0

Inbefore: I know this question has been asked more than once on this forum. I know I could post my answer on these other posts. My issue is that the users who asked those questions did it 3 - 4 years ago, and looking at their autors profile, they do not seem very active and I beleive the questions would never be marked as solved.

So I've been looking around every where to find help on doing exactly that, with no simple solutions allowing to limit the user from typing more than one dot in an input type=“number”. It is important to keep the input type="number" as it may affect the keyboard on certain mobile devices.

I know it might seem limitative to prevent a keyboard event, and some might argue that the field should only be evaluated on submit or on change or on blur. I say the input type="number" already limits the effects of certain keypresses that are anything but numbers or "e" or ".", and that happens on the fly.

On top of it, some solutions made it impossible to limit the amount of floats allowed.

Samuel Charpentier
  • 599
  • 1
  • 7
  • 28
  • 1
    That's not how Stack Overflow works. Questions are not marked as "solved". An answer may be marked as "accepted", but that only means something to the asker, no one else. The age of the question makes no difference unless there are fundamental changes in the environment (e.g., the question was specifically asking about version 1.22 and the answer requires 43.71). In this case, any question asking about `input type="number"` will be able to use or adapt the code presented here. – Heretic Monkey May 28 '20 at 17:26
  • Does this answer your question? [Prevent typing non-numeric in input type number](https://stackoverflow.com/questions/19966417/prevent-typing-non-numeric-in-input-type-number) – Heretic Monkey May 28 '20 at 17:30
  • No it doesn't as it doesn't prevent multiple dots to be typed. – Samuel Charpentier May 28 '20 at 17:33
  • It simply doesn't allow for dots to be typed, nor numbpad numbers, wich is even worse. – Samuel Charpentier May 28 '20 at 17:35
  • By "it" I assume you're referring to the accepted answer. As mentioned, the accepted answer is only relevant to the asker; it is the answer that help them the most. There are 10 other answers to that question. For example, [this one](https://stackoverflow.com/a/49780376/215552) which does everything you're looking for. – Heretic Monkey May 28 '20 at 18:15
  • Sadly not, since `e.target.value.toString()` on a number input field containing `"13."` returns `"13"` so doing `.indexOf('.')` on it is useless and will let the keypress happen... It could also prevent keyboard shortcuts with other modifiers than ctrl. preventing default in bulk is a bad accessibility solution. – Samuel Charpentier May 28 '20 at 18:34
  • So add your answer to that duplicate question. – Heretic Monkey May 28 '20 at 18:56

1 Answers1

0

You can find very detailed explainations in this Codepen

This solution is simple and will only allow one dot in the number field. It doesn't prevent from clicking back in the number and adding numbers before the ".". It doesn't prevent from executing browser keyboard shortcuts like refresh, copy and pasting (as long as the pasted value is a valid number) and others. It will allow to add "." withing the body of the number, but will remove any exceeding floats over the set limit.

The only behavior that I still can't prevent is if you press the dot key at the end of the input repeatedly, the dot will blink on and off. This happens because a typed value of "13." is a valid number and returns "13" and a typed value of "13.." is not and returns "". In my solution, if a value returns "" without the press of backspace or delete, it gets rolled back to the last valid value, wich is "13", obtained from the typed value "13.".

I've tried solutions where if a dot was pressed once, it was prevented to be triggered a second time, but every time I managed to get stuck with pressing the dot right after an already existing dot followed by nothing and then get stuck with not being able to type a dot unless I pressed any other key first.

I think the blinking on multiple press is the best solution for ther's no way a user would type a dot and nothing happens.

let lastValidInputValue;
let selectedDot = false;


const onKeypress = (e) => {
  if (e.key === "." && e.target.value.indexOf(".") !== -1 && !selectedDot) e.preventDefault();
  selectedDot = false;

  if (e.key === "e") e.preventDefault();
};


const onInput = (e) => {
  if (
    e.target.value.indexOf(".") < e.target.value.length - e.target.getAttribute("data-toFixed") - 1 &&
    e.target.value.indexOf(".") !== -1
  ) {
    let newValue;
    newValue = e.target.value.slice(
      0,
      e.target.value.indexOf(".") +
        parseInt(e.target.getAttribute("data-toFixed")) +
        1
    );
    newValue = parseFloat(newValue);
    e.target.value = newValue;
  }
  if (e.target.value !== "") {
    lastValidInputValue = e.target.value;
  } else if (e.inputType.match(/delete/g)) {
    lastValidInputValue = "";
  } else {
    e.target.value = lastValidInputValue;
  }
};

 const onSelect = (e) => {
   if(window.getSelection().toString().indexOf(".") > -1) selectedDot = true;
 }
<input type="number" id="myNumber" name="myNumber" data-toFixed="2" step="any" onkeypress="onKeypress(event)" oninput="onInput(event)" onselect="onSelect(event)">
Samuel Charpentier
  • 599
  • 1
  • 7
  • 28