What is the mechanism behind the functionality of the input type=number spinner in browsers?

I want to recreate the spinner feature found in input type=number.

<input type=number step=0.3/>

When clicking on the up arrow of the spinner, the value increases by 0.3 (0.3, 0.6 , 0.9 , 1.2 , 1.5 ...etc).

However, if the current value is 1.4 and I click increase, it jumps to 1.5 which is a 0.1 increment, while at 1.3 it's a 0.2 increment.
My question is how can I determine the closest step increment when increasing the current value?!

Below is my solution:

export class Spinner { 
        increase(currentValue, step) {
           const step = step || 1;
           const decimalSize = getDecimalSize(step);
           const current = normalize(currentValue, decimalSize);
           const ratio = Math.ceil(normalize(current / step, decimalSize));
           let increment = normalize(ratio * step, decimalSize);
         
        if (
            normalize(current % step, decimalSize) === 0 ||
            current === increment ||
            normalize(current % step, decimalSize) === 1
        ) {
            increment = normalize(current + step, decimalSize);
        }
    }
}

export function getDecimalSize(value: number) {
    if (Math.floor(value) === value) return 0;
    return value.toString().split(".")[1]?.length || 0;
}

export function normalize(value: number, decimalSize: number): number {
    decimalSize = decimalSize || 1;
   const n = Math.pow(10, decimalSize + 1);
   return Math.round(value * n) / n;
}


This logic works well for positive numbers but not as expected with negative values.

For example, when the current value is -2 with a step of 0.3, it results in -1.8 instead of -1.7

Answer №1

According to the HTML Standard

When an element is experiencing a step mismatch, the browser may decide to round the value to the nearest number that avoids this issue. In cases where there are two possible options, browsers are advised to select the one closest to positive infinity.

Your web browser is most likely adjusting the step value to prevent any mismatch. For example, it rounds 1.4 to 1.5 because 1.5 is exactly 5 steps away from 0, whereas 1.7 would be 5.666 steps away. The same logic applies to negative numbers, with 1.8 being divisible by 0.3 while 1.7 is not.

It's important to note that in some scenarios, like the case of rounding to 1.8 instead of 1.5, the actual behavior deviates from the specification. These discrepancies can occur and may potentially change in future updates.

I haven't tested this myself, but it's conceivable that different browsers could yield dissimilar results.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can you retrieve the keys of an object that conforms to an interface?

In the following demonstration, we have two objects - KEYS and KEYS2. When importing KEYS in index.ts, autocomplete suggestions are available for K1 and K2 because KEYS does not adhere to an interface. On the other hand, with KEYS2, autocomplete is not pr ...

Leveraging $http and $q in an Angular configuration with a service/provider

My goal is to load specific configurations for each controller in the app.config section. Each controller requires a distinct set of data, but these sets are not mutually exclusive. I am struggling to find a solution to this issue. .config(['$routePr ...

Make sure to retain PHP includes/requires when dynamically loading a new page using AJAX

My website has a main page with a header, navbar, footer, and content is loaded using AJAX into a div: <div id="content" class="page-content"></div> I am wondering if it's feasible to load a page dynamically into the content div using AJA ...

Issue with Electron: parent window not being recognized in dialog.showMessageBox() causing modal functionality to fail

Struggling with the basics of Electron, I can't seem to make a dialog box modal no matter what technique I try. Every attempt I make ends in failure - either the dialog box isn't modal, or it's totally empty (...and still not modal). const ...

Using Node.js to generate several MongoDB documents by iterating through JSON data submitted via POST requests

When a webpage sends JSON data via POST to my Node.js App (MEAN-environment using Mongoose), the format of the JSON file is as follows: Firstname: 'XY', Surname: 'asd', Articles: [ { title: '1', description: ...

Revitalize website when submitting in React.js

I need assistance with reloading my React.js page after clicking the submit button. The purpose of this is to update the displayed data by fetching new entries from the database. import React, {useEffect, useState} from 'react'; import axios from ...

How to efficiently upload multiple files in an array using jQuery and AJAX technology

Looking for a way to upload each file separately with HTML? <input type="file" name="[]" multiple /> Struggling to figure out how to achieve this? Here is an example: $('input:file').on('change', function(){ allFiles = $(th ...

IE8 does not support Jquery ajax() for cross domain requests to remote servers

My current script includes an ajax request to a remote server that provides a plain text response. This setup functions properly in all browsers with the exception of IE8, which is not surprising. The script snippet looks like this: $.ajax({ url: &apos ...

Authenticating with passportjs using a Google Apps email address for verification

I am currently experimenting with using Passport.js along with a Google Apps email ID. I have successfully been able to authenticate using a gmail.com email ID, however, I am facing challenges when attempting to authenticate if the email ID is associated w ...

Surprising results when a class is applied using jQuery

Exploring the differences between two fiddles (make sure to run the code on the jsfiddle pages to see the differences clearly). First Fiddle Simple demonstration: $("body").addClass("noScroll"); alert($("body").hasClass("noScroll")); $("body").removeCla ...

Tips for keeping the most recently opened accordion in a group by using the is-open attribute to call a function

I have a dynamically populated accordion that updates every 15 seconds. I need to keep track of the last opened accordion group as the dataList can be very large and parsing it for each update is not feasible. Below is the code snippet from my HTML file: ...

Customize a jQuery tab plugin to show a particular tab upon initialization [with JSFiddle example]

Currently, I am utilizing the jQuery tabs demo by Jack Moore from . However, I require a modification in the JS to initially display a specific tab (modifying the URL is not feasible for my intended scenario). I have everything prepared in this fiddle: ht ...

JavaScript fails to function properly on FireFox

I'm currently troubleshooting a script that works in Chrome but not in FireFox. I suspect it's due to the webkit syntax, so I tried converting it to a standard gradient without success. Can you help me identify what's causing the issue? Web ...

Is it possible to restrict the acceptance of certain variables from a CSV file in Django form fields, instead of accepting

In my Django model, I have two fields for latitude and longitude which are both declared as CharField. The model needs to accept multiple coordinates, so in the UI form, I enter these coordinates separated by commas. I then split this char input in a funct ...

Adjusting widths of strokes in React Native Vector Icons

I selected an icon from the react-native-vector-icon library. For instance, let's use featherIcons. How can I include a stroke-width property to the react-native-vector-icons package? import FeatherIcon from 'react-native-vector-icons/Feather&ap ...

Using the Strikethrough Feature in React

Is it possible to apply a strikethrough effect to a checkbox's label text when toggled on and off? In my system development process, I've utilized various methods. What modifications should be made in the fComplete method for this feature to wor ...

Tips for preventing HTTP Status 415 when sending an ajax request to the server

I am struggling with an AJAX call that should be returning a JSON document function fetchData() { $.ajax({ url: '/x', type: 'GET', data: "json", success: function (data) { // code is miss ...

There was no popcorn mix-up in your document

Encountering an issue while using grunt for staging (grunt serve): Running "bower-install:app" (bower-install) task popcornjs was not injected into your file. Please check the "app\bower_components\popcornjs" directory for the required file, an ...

Turn off hover effect for the v-checkbox component in Vuetify 2

Is there a way to prevent the darkened circle from appearing behind a v-checkbox in Vuetify 2 when hovering over it? My v-checkbox is currently enclosed within a v-tab and a v-tooltip, although I'm not sure if that has any impact. <v-tab v-for=&quo ...

Triggering event within the componentDidUpdate lifecycle method

Here is the code snippet that I am working with: handleValidate = (value: string, e: React.ChangeEvent<HTMLTextAreaElement>) => { const { onValueChange } = this.props; const errorMessage = this.validateJsonSchema(value); if (errorMessage == null ...