0

I was building a website and came across a problem.

The problem is, when users are asked to enter a price, they can type not only numbers but whatever they want. I want to restrict users to type numbers only in that input field. I've tried type=number and pattern="[0-9]+" attributes and gave it a style of appearence: textfield (Because it shows an uparrow and a downarrow when shown in browser) but it doesn't seem to work. I suppose there is a solution for this with javascript.

HTML :

<input class="input-price" type="number">

CSS:

input[type="number"] {
  appearance: textfield;
}

input[type="number"] {
  appearance: textfield;
}
<input class="input-price" type="number">

Hopefully, someone will give me a solution. Thanks in advance!

Not A Bot
  • 2,474
  • 2
  • 16
  • 33
Jose Saji
  • 3
  • 1
  • 3
  • Does this answer your question? [Why does the html input with type "number" allow the letter 'e' to be entered in the field?](https://stackoverflow.com/questions/31706611/why-does-the-html-input-with-type-number-allow-the-letter-e-to-be-entered-in) – Mr T May 18 '21 at 10:27

2 Answers2

0

I don't know what browser your users use that can bypass restrictions in the numerical input, but in the days when not all browsers supported type="number" I've used something like this:

const inputs = document.querySelectorAll('input[data-type="number"]');
for(let i = 0; i < inputs.length; i++)
{
  inputs[i].addEventListener("input", onInput);
  inputs[i].addEventListener("keydown", onKeydown);
  inputs[i].nextSibling.addEventListener("mousedown", onMousedown);
  inputs[i].nextSibling.addEventListener("mouseup", onMouseup);
}

function onInput(e)
{
  clearTimeout(this._timer);
  const input = e.target,
        min = input.min === "" ? null : Number(input.min),
        max = input.max === "" ? null : Number(input.max),
        canMinus = min === null || min < 0,
        canPoint = input.hasAttribute("nopoint") ? false : true;

  let curStart = input.selectionStart,
      dot = 0,
      minus = 0,
      i = 0,
      timeout = null,
      val = input.value;

  val = val.replace(/[^0-9]/g, (char, pos) =>
  {
    if (      (char == "-" && canMinus && !(pos-i) && --minus)
          ||  ((char == "." || char == ",") && canPoint && pos && (pos-i > 1 || !minus) && !dot++))
    {
      return char;
    }

    if (i < curStart)
      curStart--;

    i++;
    return "";
  });

  if (curStart < 0)
    curStart = 0;

  const isNumber = val.match(/[0-9]/);
  
  if (isNumber && min !== null && val < min)
    val = timeout = min;

  if (isNumber && max !== null && val > max)
    val = timeout = max;

  const exec = () =>
  {
    input.value = val
    input.selectionStart = curStart;
    input.selectionEnd = curStart;
  }
  if (timeout === null)
    return exec();

  this._timer = setTimeout(exec, 1000);
}

function onKeydown(e)
{
  const input = e.target,
        min = input.min === "" ? null : Number(input.min),
        max = input.max === "" ? null : Number(input.max),
        step = input.step === "" ? 1 : Number(input.step),
        keys = {ArrowUp: step, ArrowDown: -step};

  if (keys[e.key])
  {
    let val = (keys[e.key] == ~~keys[e.key] ? ~~input.value : Number(input.value)) + keys[e.key];
    if (min !== null && val < min)
      val = min;

    if (max !== null && val > max)
      val = max;

    input.value = val;
    return e.preventDefault();
  }
}

function onMousedown(e)
{
  if (e.target.parentNode.previousSibling.tagName != "INPUT")
    return;

  const that = this,
        obj = {
          target: e.target.parentNode.previousSibling,
          key: (e.target.previousSibling === null) ? "ArrowUp" : "ArrowDown",
          preventDefault: ()=>{}
        };

  let time = 300;

  !function loop()
  {
    onKeydown(obj);
    that._timer = setTimeout(loop, time);
    time = 30;
  }();
}

function onMouseup(e)
{
  clearTimeout(this._timer);
}
input[data-type="number"] {
  appearance: textfield;
  padding-right: 1.2em;
}
input[data-type="number"]:not(:focus):not(:hover) ~ span:not(:focus):not(:hover)
{
  visibility: hidden;
}
input[data-type="number"] ~ span
{
  float:right;
  position: relative;
  right: 1.8em;
  top: 0.2em;
  height: 1.8em;
  width: 1.5em;
  line-height: 1em;
  font-size: 0.6em;
  cursor: default;
  display: grid;
  background-color: #F1F1F1;
  user-select: none;
}
input[data-type="number"] ~ span > span:nth-child(1):before,
input[data-type="number"] ~ span > span:nth-child(2):before
{
  transform: scaleY(0.6);
  display: block;
}

input[data-type="number"] ~ span > span:nth-child(1):before
{
  content: "▲";
}
input[data-type="number"] ~ span > span:nth-child(2):before
{
  content: "▼";
}

input[data-type="number"] ~ span > span:nth-child(1),
input[data-type="number"] ~ span > span:nth-child(2)
{
  color: #505050;
  width: 100%;
  text-align: center;
}
input[data-type="number"] ~ span > span:nth-child(1):hover,
input[data-type="number"] ~ span > span:nth-child(2):hover
{
  background-color: #D2D2D2;
}
input[data-type="number"] ~ span > span:nth-child(1):active,
input[data-type="number"] ~ span > span:nth-child(2):active
{
  color: white;
  background-color: #787878;
}
input[data-type="number"] ~ span > span:nth-child(1)
{
  top: 0;
}
input[data-type="number"] ~ span > span:nth-child(2)
{
  bottom: 0;
}
<table>
  <tr>
    <td>Native type="number"</td>
    <td><input class="input-price" type="number"></td>
  </tr>
  <tr>
    <td>Any number</td>
    <td><input class="input-price" data-type="number"><span><span></span><span></span></span></td>
  </tr>

  <tr>
    <td>Min 11 / max 123</td>
    <td><input class="input-price" data-type="number" min="11" max="123"><span><span></span><span></span></span></td>
  </tr>

  <tr>
    <td>No decimal point</td>
    <td><input class="input-price" data-type="number" nopoint=""><span><span></span><span></span></span></td>
  </tr>
</table>
vanowm
  • 9,466
  • 2
  • 21
  • 37
0

type="number" is only available to HTML5 compliant browsers. There is an answer available in an overflow already, for getting the result you are after using Javascript here.