HTML5 input type=number change step behavior

If I use an input field of type="number" with step="100". I don't want odd numbers to be invalid. I just want increase or decrease to be with a value of 1000.

<input type="number" min="100" max="999999" step="100" />

If the user enters the value "199" and submits, he/she gets an error because the value isn't dividable by 100. But all I want with the step-value is to control the behavior of the spinner, e.g. if the user click up I want the value 199 to become 200 and if he/she clicks down I want it to become 100. Or ideally I would like the value to be increased or decreased with a value of 100.

How do I do this? I tried using the invalid event (with jQuery 1.7.2) like this:

$( "[type='number']" ).bind( 'invalid', function( e ) {
    var el = $( this ),
      val = el.val( ),
      min = el.attr( 'min' ),
      max = el.attr( 'max' );

    if ( val >= min && val <= max ) {
        return false;
    }
} );

But this results in the form not being submitted.

PS.: This is in Google Chrome 20.0.1132.57 on Fedora 16.

Answers


I don't think you can, the step and the validation are closely tied together. In the future you may be able to override the stepUp() and stepDown() functions to get the behaviour you describe but I've not researched if this is an intended use case for these things. I would recommend posting on the WHATWG mailing list, asking specifically about those functions and describing your use case.

For current practical purposes have you tried setting step=1 and binding to a click event to capture? I can see there'll probably be an issue with distinguishing between 'up' and 'down' clicks but that might be surmountable. It might be easier all round however to use a text input, set the pattern attribute appropriately and implement your own spinner.


Well, first of all thank you for this very interesting question. I've learned a lot about HTML5 validation by searching a solution to your problem.

My research led me to find that HTML5 form validation API has an interesting set of properties that are read-only, but very useful to do what you want.

My approach to your problem was to first add the novalidate attribute to the form element, so that I can control when to trigger the validation, then read the validity object attached to the input, so I can know exactly what validation errors are there, and if the only error is stepMismatch (this is what triggers invalidity of numbers such as 199), I can bypass all the validation process. Else, I can show normal HTML validation behaviour with the reportValidity() method.

Here is the code I came up with that I hope does what you want :

var form = document.querySelector("form")       // Get the form
var input = document.querySelector("#myInput")  // Get the input to validate

form.addEventListener("submit", function(e) {
	e.preventDefault()  // Catch the submit
  // Do the magic
  if(onlyStepMatters(input.validity)){
  	form.submit()
  }else {
  	form.reportValidity()
  }
})

function onlyStepMatters(validityState) {
  return !(
    validityState.badInput || validityState.customError || validityState. patternMismatch || validityState.rangeOverflow || validityState.rangeUnderflow || validityState.tooLong || validityState.tooShort || validityState.typeMismatch || validityState.valueMissing
    )
    
  /* This is what the object looks like, notice I just skipped the stepMismatch */
  /*
  {
    badInput,
    customError,
    patternMismatch,
    rangeOverflow,
    rangeUnderflow,
    stepMismatch,
    tooLong,
    tooShort,
    typeMismatch,
    valid,
    valueMissing,
  }
  */
}
<form novalidate>
<input type="number" id="myInput" min="0" max="1000" step = "100" placeholder="Enter a number" required/>
<button type="submit">Send</button> 
</form>

Only with step but you can use range and add marks to slider then use javascript to check the near value and set it

like

function updateTextInput(val) {
  if      (val > 36)  val = 50;
  else if (val > 26)  val = 36;
  else if (val > 20)  val = 26;
  else if (val > 15)  val = 20;
  else if (val >  1)  val = 15;
  document.getElementById('textInput').value = val;
}
<input type='range' min='0' max='50' step='1' name='slide' list="settings" onchange="updateTextInput(this.value);">
<datalist id="settings">
    <option>15</option>
    <option>20</option>
    <option>26</option>
    <option>36</option>
    <option>50</option>
</datalist>
<input type="text" id="textInput" />

Here is the needed code to achieve what you asked, I tried to avoid recalculations and changing standard behavior so this should be transparent to any other constrain one would set (required, pattern, ranges ...).

I only tested on Firefox and Chrome but I believe it should work on any recent browser.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Disable validity constrains</title>
<!-- avoid highlight due to constrains -->
<style>#qty { box-shadow: unset; }</style>
</head>
<body>
<form>
        <input id="qty" name="num" type="number" min="100" max="1000" step="100">
        <input type="submit">
</form>
<script>
(function() {
        var el = document.getElementById('qty');
        el && el.addEventListener('invalid', function(event) {
                if ('validity' in el) {
                        for (var state in el.validity) {
                                if (state == 'stepMismatch') { continue; }
                                if (el.validity[state]) { return true; }
                        }
                        event.preventDefault();
                        el.form.submit();
                }
        }, true);
})();
</script>
</body>
</html>

inside "change" event round to nearest valid value.

$( "[type='number']" ).change(function () {
   var value = $(this).val());
   var newValue = Math.round(value/100)*100
   if (newValue < 100) {
      newValue = 100;
   }
   if (newValue > 999999) {
      newValue = 999999;
   }
   if (newValue === value ) {
      return;
   }
   $(this).val(newValue)
})

Try using change() function ...

<form>
    <input type="number" id="number" min="100" max="999999" step="100" />
</form>

$(document).ready(function(){
    $("#number").change(function(a){
        if ($(this).val() % 100 === 0) {
            /* Do something when the number is even */ 
            console.log("EVEN");
        } else {
            /* Or when it's odd (isn't dividable by 100), do ... whatever */ 
            console.log("ODD");
        }
    })
});

And I was testing it using bootstrap, like the example you want, when user click up the value will become 200 (from 199), when the user click down the value will become 100

To change dividable :

if ($(this).val() % 100 === 0) { //Change 100 with what ever you want

I've been playing a little with your code, trying to achieve this:

ideally I would like the value to be increased or decreased with a value of 100

… and ended-up with some script to:

  • Detect a step increase or decrease by keyboard or mouse,
  • Use a remainder variable to store the result of the calculation: value % 100,
  • Modify the step parameter and the value of the input,
  • Add back the remainder variable when keyboard or mouse event has ended, and reset the step parameter back to 1 (needed to be able to submit).

In this working snippet, try to modify the input value with the arrows (on keyboard or with the mouse), starting with a number not divisable by 100:

var remainder = false;
var stepbefore;

$("[type='number']").bind('keydown mousedown', function() {
  // If keyboard Up or Down arrow keys or mouse left button on the arrows
  if (event.keyCode == 38 || event.keyCode == 40 || event.button === 0) {
    // If there is already a change running, exit the function
    if (remainder !== false) return;

    var myStep = this.getAttribute("stepcustom"); // Get my "stepcustom" attribute
    remainder = this.value % myStep;
    this.value = Math.floor(this.value / myStep) * myStep;
    stepbefore = this.step;
    this.step = myStep;
  }
});

$("[type='number']").bind('keyup mouseup', function() {
  if (remainder !== false) {
    this.value = +(this.value) + remainder;
    this.step = stepbefore;
    remainder = false;
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<form>
  <input type="number" min="100" max="999999" step="1" stepcustom="100" />
  <input type="submit" />
</form>

This is a simple javascript function that could help you out where prev_num is global variable this will work for increment and decrement both

var perv_num=0;
function ax(z)
{

     let def;
     let mul=10;
     let valu;
     let valucopy=parseInt(z.value);
     let  frst;
     valucopy=((valucopy+100)%100);


         if (parseInt( z.value)<100) 
         {
                  document.getElementById("myNumber").value="";
                  document.getElementById("myNumber").value=100;
         }
         else if(parseInt( z.value)<perv_num)
        {
                  def=parseInt( z.value.length);
                  mul=Math.pow(mul,def-1);
                  frst=(parseInt(z.value[0])*mul);
                  document.getElementById("myNumber").value="";
                  document.getElementById("myNumber").value=frst;
         }
         else if(valucopy  ==0)
         {
                  document.getElementById("myNumber").value="";
                 document.getElementById("myNumber").value=parseInt(z.value)+100;
          }
          else{
                  def=parseInt( z.value.length);
                  mul=Math.pow(mul,def-1);
                  frst=(parseInt(z.value[0])*mul);
                  valu=Math.abs( parseInt(z.value)-frst);
                  valu=100-valu;
                  var number=(parseInt(z.value)+valu);
                  document.getElementById("myNumber").value="";
                  document.getElementById("myNumber").value= number;
              }
              perv_num=parseInt( z.value);
}

and html is like

    <input type="number" id="myNumber" onchange="ax(this)">

fiddle is https://jsfiddle.net/ecpy5gr0/1


I make script to change step attribute dynamically:

/*
  jQuery Optional Number Step

Version: 1.0.0
 Author: Arthur Shlain
   Repo: https://github.com/ArthurShlain/JQuery-Optional-Step
 Issues: https://github.com/ArthurShlain/JQuery-Optional-Step/issues
*/
(function ($) {

    $.fn.optionalNumberStep = function (step) {
        var $base = $(this);

        var $body = $('body');

        $body.on("mouseenter mousemove", '[data-optional-step]', function () {
            $(this).attr("step", $(this).attr('data-optional-step'));
        });

        $body.on("mouseleave blur", '[data-optional-step]', function () {
            $(this).removeAttr("step");
        });

        $body.on("keydown", '[data-optional-step]', function () {
            var key = event.which;
            switch (key) {
                case 38: // Key up.
                    $(this).attr("step", step);
                    break;

                case 40: // Key down.
                    $(this).attr("step", step);
                    break;
                default:
                    $(this).removeAttr("step");
                    break;
            }
        });

        if (step === 'unset') {
            $base.removeAttr('data-optional-step');
        }

        if ($.isNumeric(step)) {
            $base.attr('data-optional-step', step);
        }

    }

}(jQuery));

jQuery(function() {
  $('.optional-step-100').optionalNumberStep(100);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container mt-5">
  <h1>JQuery Optional Number Step</h1>
  <div class="form-group" style="max-width: 300px">
    <label>Example</label>
    <input type="number" class="form-control optional-step-100" value="0">
    <small id="emailHelp" class="form-text text-muted">Dynamic step for this field is 100
      <br>You can specify any numeric value on keyboard. 
      <br>HTML5 step validation will not be applied.</small>
  </div>
  <a class="btn btn-dark btn-sm" href="https://github.com/ArthurShlain/JQuery-Optional-Step" target="_blank">View on GitHub</a>
</div>

Need Your Help

Dynamically Add Google Map V3 Markers using jQuery

javascript jquery ajax google-maps google-maps-api-3

I am adding markers to Google Maps dynamically using jquery and Google Maps V3 API. When the button search_button is clicked, a request is sent to the server using AJAX, which returns a JSON array of

recommended way to make a 2d HUD in webgl

javascript performance html5 webgl

For a game in webgl where fps performance is important, what is the most efficient way to make a 2D HUD? I can think of 3 options, but its unclear to me what is the performance cost of each and which