Better numeric inputs in desktop browsers

Updated . Posted . Visible to the public. Repeats.

You want to use <input type="number"> fields in your applications.
However, your desktop users may encounter some weird quirks:

  1. Aside from allowing only digits and decimal separators, an "e" is also allowed (to allow scientific notation like "1e3").
    • Non-technical users will be confused by this.
    • Your server needs to understand that syntax. If it converts only digits (e.g. to_i in Ruby) you'll end up with wrong values (like 1 instead of 1000).
  2. Users can change values with the up and down arrow keys.
    • This can be useful if users know about it.
    • To most users, this is often unexpected (text inputs look alike and move the cursor to start/end) and they will rather change values accidentally than on purpose.
  3. Some browsers (at least Firefox) will change values when you scroll the mouse wheel while your mouse pointer hovers the (focused) input.
    • This is terrible. Nobody wants that.
    • Users will change values by accident when trying to scroll the page.
  4. Some browsers (at least Firefox) still allow regular letters for numeric inputs (as if specifying inputmode="numeric").
    • Users might enter values like "20€".
    • Firefox parses that as an empty input value, leading to validation errors and thus user confusion.

We can fix all that with JavaScript:

const element = document.querySelector('#your-input')

// Number inputs allow incrementing/decrementing with arrow keys, and support exponential notation
// (in some browsers even regular letters). We want neither.
element.addEventListener('keydown', (event) => {
  const key = event.key
  const nonNumberKey = key.length === 1 && !event.ctrlKey && !event.altKey && !key.match(/[0-9\-+,.]/)
  if (key === 'ArrowUp' || key === 'ArrowDown' || nonNumberKey) {
    event.preventDefault()
  }
})

// Some browser (at least Firefox) will increment/decrement a number when using the mouse's scroll wheel
// above a focused number input. Since this can lead to users changing values by accident, we want to avoid that.
element.addEventListener('wheel', (event) => {
  if (element.matches(':focus')) {
    event.preventDefault()
  }
})

You can easily wrap this code your favorite frontend framework.

Additionally, you can hide the number field's up/down arrow buttons with CSS:

input[type="number"] {
  -moz-appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
}

See https://codepen.io/foobear/pen/dyzyBBX Show archive.org snapshot for a demo page.

Arne Hartherz
Last edit
Arne Hartherz
License
Source code in this card is licensed under the MIT License.
Posted by Arne Hartherz to makandra dev (2021-10-11 08:29)