javascripthtmlinputuser-experience

Can I have an HTML number input's arrows jump different than its step attribute?


I have an <input type="number" step="1"> element for choosing font size. However, given the context of drawing on a large canvas, changing the number by 1 is barely noticeable. It would be more convenient for users if they could step up or down by 5 or 10 each time they click the arrow.

enter image description here

I still need to keep the step attribute at 1 because the input should remain valid even with fine-grained values. Is there a way to achieve this behavior, like the below (pseudo) jump attribute. I am open to Javascript solutions too but AFAIK there is no such event for when user clicks the arrows.

<input type="number" min="1" max="1000" step="1" jump="5" value="100" />


Solution

  • You could try this: The input event checks if the change in value matches the step value and adjusts it by the jump value accordingly.

    Edit: In this case, to answer to your comment we will use a flag and add back the keydown event.

    Add a condition to the input event listener to prevent that unwanted behaviour in case you manually change the value

    document.addEventListener('DOMContentLoaded', () => {
      const input = document.querySelector('input[type="number"][min="1"][max="1000"][step="1"]');
    
      const jump = parseInt(input.getAttribute('jump'), 10);
      let previousValue = parseInt(input.value, 10);
      let isManualChange = false;
    
      input.addEventListener('keydown', (event) => {
        if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
          event.preventDefault();
          const currentValue = parseInt(input.value, 10);
    
          if (event.key === 'ArrowUp') {
            input.value = currentValue + jump;
          } else if (event.key === 'ArrowDown') {
            input.value = currentValue - jump;
          }
    
          previousValue = parseInt(input.value, 10);
          isManualChange = false;
        } else {
          isManualChange = true;
        }
      });
    
      input.addEventListener('input', (event) => {
        const currentValue = parseInt(input.value, 10);
        const step = parseInt(input.getAttribute('step'), 10);
    
        if (!isManualChange && Math.abs(currentValue - previousValue) === step) {
          if (currentValue > previousValue) {
            input.value = previousValue + jump;
          } else {
            input.value = previousValue - jump;
          }
        }
    
        previousValue = parseInt(input.value, 10);
      });
    });
    <input type="number" min="1" max="1000" step="1" jump="5" value="100" />