38

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.

Takit Isy
  • 9,688
  • 3
  • 23
  • 47
mabs
  • 987
  • 1
  • 10
  • 17
  • I'm not sure you're going to be able to work around this. You might want to consider using jQuery UI's spinner instead (http://tjvantoll.com/2012/07/15/native-html5-number-picker-vs-jquery-uis-spinner-which-to-use/). – TJ VanToll Jul 31 '12 at 14:43
  • Also this might be helpful - http://stackoverflow.com/questions/3090369/disable-validation-of-html5-form-elements. – TJ VanToll Jul 31 '12 at 14:49
  • @mabs Did you check my answer? I think I managed to make it work like you "ideally" wanted. – Takit Isy Jul 31 '18 at 11:56
  • 1
    @TakitIsy sorry I haven't checked it out yet. The question is 6 years old and I had forgotten all about it. – mabs Jul 31 '18 at 21:59

8 Answers8

9

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>

I'm pretty sure this code could be refactored and become more concise based on the same logic, but I don't have enough time to think more about it.

Any constructive comment will be appreciated.

Hope this helps.

Vignesh Raja
  • 7,927
  • 1
  • 33
  • 42
Rafik Tighilt
  • 2,071
  • 1
  • 15
  • 27
  • this is a good idea but probably using `setCustomValidity()` instead of catching the form submit event would be cleaner – phil294 Sep 10 '19 at 20:25
  • I've tried that one back then, but I can't remember why it was causing problems. – Rafik Tighilt Sep 11 '19 at 18:01
  • This is good if you have only a control, but adding `novalidate` to the entire form causes you'll have to validate all the other controls manually! It would be better to have the validation disabled only on the specific input element. – virtualdj May 22 '21 at 14:48
7

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.

robertc
  • 74,533
  • 18
  • 193
  • 177
2

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>

Hope it helps.

Takit Isy
  • 9,688
  • 3
  • 23
  • 47
  • @hev1 Now it is. Thanks for the comment. :) – Takit Isy Jul 29 '18 at 08:08
  • You can improve performance a bit by getting rid of the second `$("[type='number']")` and attaching the bind directly to the first one, as well as only binding to inputs that have the correct attribute (eg: `$("input[type='number'][stepcustom]").bind(...).bind(...)`) – Tofandel Feb 05 '20 at 10:47
1

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" />
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Nuno Dias
  • 27
  • 1
0

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>
Diego Perini
  • 8,003
  • 1
  • 18
  • 9
0

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)
})
Sergey Gurin
  • 1,537
  • 15
  • 14
0

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
0

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

Ricky
  • 114
  • 1
  • 10