0

On some browsers, inputs like date, time, datetime-local, number are not implemented and are considered as text inputs.

I made these RegExp to check these input values :

const RegExpYear = '(000[1-9]|00[1-9]\\d|0[1-9]\\d\\d|100\\d|10[1-9]\\d|1[1-9]\\d{2}|[2-9]\\d{3}|[1-9]\\d{4}|1\\d{5}|2[0-6]\\d{4}|27[0-4]\\d{3}|275[0-6]\\d{2}|2757[0-5]\\d|275760)',
      RegExpMonth ='(0[1-9]|1[012])',
      RegExpDay = '(0[1-9]|[12]\\d|3[01])',
      RegExpHour = '(0\\d|1\\d|2[0-4])',
      RegExpMinSec = '(0\\d|[1-5]\\d)',
      RegExpMilli = '(00\\d|0[1-9]\\d|[1-9]\\d{2})',
      patternWeek = new RegExp('^'+RegExpYear+'-W(([1-4][0-9])|(5[0-3])|0[1-9])$'),
      patternMonth = new RegExp('^'+RegExpYear + '-' + RegExpMonth + '$'),
      patternDateTimeLocal = new RegExp('^' + RegExpYear + '-' + RegExpMonth + '-' + RegExpDay + 'T' + RegExpHour + ':' + RegExpMinSec + '(?::' + RegExpMinSec + ')?(?:\.' + RegExpMilli + ')?$'),
      patternDate = new RegExp('^' + RegExpYear + '-' + RegExpMonth + '-' + RegExpDay + '$'),
      patternTime = new RegExp('^' + RegExpHour + ':' + RegExpMinSec + '(?::' + RegExpMinSec + ')?(?:\.' + RegExpMilli + ')?$'),
      patternNumber = new RegExp('^-?\d+\.?(\d+)?([eE][+-]?\d+)?$');

document.querySelectorAll('[type=datetime-local], [type=date], [type=time], [type=month], [type=week], [type=number]').forEach(function(a){
  let type = a.type,
      attrtype = a.attributes.getNamedItem('type').value,
      pattern;

  if (type !== attrtype) {

    switch(attrtype) {
      case 'number' : pattern = patternNumber; break;
      case 'week' : pattern = patternWeek; break;
      case 'month' : pattern = patternMonth; break;
      case 'datetime-local' : pattern = patternDateTimeLocal; break;
      case 'date' : pattern = patternDate; break;
      case 'time' : pattern = patternTime; break;
    }


    a.addEventListener('input',function(e){
      let test = pattern.test(this.value);

      if (test) {
        a.classList.add('valid')
        a.classList.remove('invalid')
      } else {
        a.classList.add('invalid')
        a.classList.remove('valid')
      }
    });
  }
});

This way I can style the wrong entries but can't prevent form submit.

Is it possible to add a constraint that passes through the ValidityState property ?

Ann MB
  • 146
  • 1
  • 13
  • Why not use HTML [type attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) on elements (date, number, etc). And a 3rd party validation plug ([Bootstrap](https://getbootstrap.com/docs/5.0/forms/validation/), for example) – devlin carnate Jan 25 '22 at 19:47
  • Because when the browser does not recognize the input type, the `type` property in automatically `text`. That's why I use `input.attributes.getNamedItem('type')`. Plus for some reasons I don't want to use a library – Ann MB Jan 25 '22 at 19:50
  • Does this answer your question? [JavaScript code to stop form submission](https://stackoverflow.com/questions/8664486/javascript-code-to-stop-form-submission) – devlin carnate Jan 25 '22 at 19:51
  • It indeed does. I'm doing a plugin so I may have to do `input.closest('form').querySelector('[type=submit]').disabled = true` or `input.closest('form').addEventListener('submit', ....` if it doesn't mess up an other validation function or another event listener on submit ? – Ann MB Jan 25 '22 at 19:58

1 Answers1

3

You won't have to revalidate all the inputs or prevent form submission inside onsubmit event handler, if you use the inbuilt DOM Constraint Validation API. Every input element validates itself and reports to form if it's valid or invalid. Special handling can be done inside onInput event handler.
Demo:

const fruitRegx = "(?!\s*[Mm]ango).*" // we don't want mango

// set patterns on all required input elements
let inpt = document.getElementById('choose');
inpt.setAttribute("pattern", fruitRegx);

// set input handlers
inpt.addEventListener('input', function(e) {
  if (inpt.validity.patternMismatch) {
    inpt.setCustomValidity("I said no mango!!");
    // if pattern doesn't mismatch report it
    // rest is taken care of automatically
    inpt.reportValidity();
  } else {
    inpt.setCustomValidity("");
  }
});

// this one has no pattern assigned, so doing validation manually
choose2.addEventListener('input', function(e) {
  let val = e.target.value;

  if (val.includes("banana")) {
    e.target.setCustomValidity("How dare you!!");
    e.target.reportValidity();
  } else {
    e.target.setCustomValidity("");
  }
});

function send(event) {
  // mimicking form submit
  output.innerHTML += `Requesting ${choose.value} and ${choose2.value}...<br>`;

  // just for the demo preventing form submission
  event.preventDefault();
  return false;
}
input:invalid {
  background-color: rgb(255, 196, 196);
}

input:valid {
  background-color: lightgreen;
}
<form onsubmit="send(event)" target="_self">
  <label for="choose">What fruit would you prefer? Except mango!</label><br>
  <input id="choose" required><br><br>
  <label for="choose">This one doesn't have pattern validation. But don't enter 'banana'!</label><br>
  <input id="choose2" required><br><br>
  <button>Submit</button>
  <pre id=output></pre>
</form>

If you type 'mango' in above demo, send method doesn't get called.
Also note how the <input> automatically gets :valid and :invalid CSS pseudo classes. So the styling is taken care of automatically.

If you are looking for solution to prevent form submission then above demo is already doing it. Fore more refer https://stackoverflow.com/a/8664680/15273968

the Hutt
  • 16,980
  • 2
  • 14
  • 44