How to set custom validation messages for HTML forms?
Asked Answered
G

18

457

I've got the following HTML form: http://jsfiddle.net/nfgfP/

<form id="form" onsubmit="return(login())">
<input name="username" placeholder="Username" required />
<input name="pass"  type="password" placeholder="Password" required/>
<br/>Remember me: <input type="checkbox" name="remember" value="true" /><br/>
<input type="submit" name="submit" value="Log In"/>

Currently when I hit enter when they're both blank, a popup box appears saying "Please fill out this field". How would I change that default message to "This field cannot be left blank"?

The type password field's error message is simply *****. To recreate this give the username a value and hit submit.

Glaze answered 11/3, 2011 at 11:40 Comment(3)
Why not just accept the browser default message? That's what users see for every other site they visit, you'll just confuse your users by creating a non-standard message. (Google has probably managed more UX evaluation & testing in determining that wording than you have!).Dismantle
@Dismantle What about multilanguage sites?Polymyxin
In my case I want to check that the value is a number before posting but I can't use the type="number" attribute (for reasons.) So I set the pattern attribute to check for numbers and optional decimals which gives the message, "Please match the requested format," on error. I'd rather it said, "Ye must gift us a number bucko."Infrastructure
B
340

Use setCustomValidity:

document.addEventListener("DOMContentLoaded", function() {
    var elements = document.getElementsByTagName("INPUT");
    for (var i = 0; i < elements.length; i++) {
        elements[i].oninvalid = function(e) {
            e.target.setCustomValidity("");
            if (!e.target.validity.valid) {
                e.target.setCustomValidity("This field cannot be left blank");
            }
        };
        elements[i].oninput = function(e) {
            e.target.setCustomValidity("");
        };
    }
})

I changed to vanilla JavaScript from Mootools as suggested by @itpastorn in the comments, but you should be able to work out the Mootools equivalent if necessary.

If setCustomValidity is set to anything other than the empty string it will cause the field to be considered invalid; therefore you must clear it before testing validity, you can't just set it and forget.

As pointed out in @thomasvdb's comment below, you need to clear the custom validity in some event outside of invalid otherwise there may be an extra pass through the oninvalid handler to clear it.

Bunkmate answered 11/3, 2011 at 18:14 Comment(4)
Why when I replace elements[i] to $("#name"), it not work? And if I set 2 validation, type='number' required for example, can I set custom message different for they?Sherris
@Sherris because you are not accessing the actual DOM object. $("") returns an array of objects, even if there is only one. $("")[i] is most likely what you want.Outsert
I'm not saying this code is or isn't good however the point of having form validation in HTML5 is to not require JavaScript for form validation hence using the oninvalid attribute even for "purists" is utterly pointless.Jinnah
This works fine for input with required attribute. However it is not applicable for input type="email". After having input the first character of your email address you fall back on standard error message "Please includ '@' to ..." I tried to trap this with else if (e.target.validity.typeMismatch) { e.target.setCustomValidity("My own message"). No success. Any suggestion ?Embry
F
495

Here is some code to display a custom error message:

<input type="text" id="username" required placeholder="Enter Name"
       oninvalid="this.setCustomValidity('Enter User Name Here')"
       oninput="this.setCustomValidity('')"/>

This part is important because it hides the error message when the user inputs new data:

oninput="setCustomValidity('')"

Note: the this keyword is not required for inline event handlers, but you may want to use it anyway for consistency.

Fictive answered 25/11, 2013 at 9:52 Comment(1)
Note that you can even omit the this. in this case because inline event handlers run with a with(this) (not saying you should)Headward
B
340

Use setCustomValidity:

document.addEventListener("DOMContentLoaded", function() {
    var elements = document.getElementsByTagName("INPUT");
    for (var i = 0; i < elements.length; i++) {
        elements[i].oninvalid = function(e) {
            e.target.setCustomValidity("");
            if (!e.target.validity.valid) {
                e.target.setCustomValidity("This field cannot be left blank");
            }
        };
        elements[i].oninput = function(e) {
            e.target.setCustomValidity("");
        };
    }
})

I changed to vanilla JavaScript from Mootools as suggested by @itpastorn in the comments, but you should be able to work out the Mootools equivalent if necessary.

If setCustomValidity is set to anything other than the empty string it will cause the field to be considered invalid; therefore you must clear it before testing validity, you can't just set it and forget.

As pointed out in @thomasvdb's comment below, you need to clear the custom validity in some event outside of invalid otherwise there may be an extra pass through the oninvalid handler to clear it.

Bunkmate answered 11/3, 2011 at 18:14 Comment(4)
Why when I replace elements[i] to $("#name"), it not work? And if I set 2 validation, type='number' required for example, can I set custom message different for they?Sherris
@Sherris because you are not accessing the actual DOM object. $("") returns an array of objects, even if there is only one. $("")[i] is most likely what you want.Outsert
I'm not saying this code is or isn't good however the point of having form validation in HTML5 is to not require JavaScript for form validation hence using the oninvalid attribute even for "purists" is utterly pointless.Jinnah
This works fine for input with required attribute. However it is not applicable for input type="email". After having input the first character of your email address you fall back on standard error message "Please includ '@' to ..." I tried to trap this with else if (e.target.validity.typeMismatch) { e.target.setCustomValidity("My own message"). No success. Any suggestion ?Embry
N
116

It's very simple to control custom messages with the help of HTML5 event oninvalid

Here is code:

<input id="UserID"  type="text" required="required"
       oninvalid="this.setCustomValidity('Witinnovation')"
       onvalid="this.setCustomValidity('')">

This is most important:

onvalid="this.setCustomValidity('')"
Nylon answered 13/8, 2012 at 12:50 Comment(5)
Also check other validations as well such as pattern, min/max values, etc... sanalksankar.blogspot.com/2010/12/…Housemaster
This causes a bug in firefox where it validates upon refresh, yet fails upon change and correction.Robbierobbin
@RyanCharmley by using onchange="this.setCustomValidity('')" the bug will be gone. Check my answer below.Signalman
If this does not work (it does not in Safari, for example), use oninput instead of onvalid.Neufer
@Neufer is right about this, oninput is the more universal solution, onvalid did not work even in Chrome for me.Reformed
H
73

Note: This no longer works in Chrome, not tested in other browsers. See edits below. This answer is being left here for historical reference.

If you feel that the validation string really should not be set by code, you can set you input element's title attribute to read "This field cannot be left blank". (Works in Chrome 10)

title="This field should not be left blank."

See http://jsfiddle.net/kaleb/nfgfP/8/

And in Firefox, you can add this attribute:

x-moz-errormessage="This field should not be left blank."

Edit

This seems to have changed since I originally wrote this answer. Now adding a title does not change the validity message, it just adds an addendum to the message. The fiddle above still applies.

Edit 2

Chrome now does nothing with the title attribute as of Chrome 51. I am not sure in which version this changed.

Heavyladen answered 18/4, 2011 at 22:47 Comment(5)
I don't believe that the title attribute should be the best place for this. Perhaps the browsers should implement some other attribute and agree on it.Heavyladen
For declarative error messages in Firefox use the attribute: x-moz-errormessage="This field should not be left blank."Bunkmate
This adds a descriptive message under the "Please fill out this field".Conjecture
@Conjecture This seems to have changed since I originally wrote this answer.Heavyladen
@Heavyladen This does work in v.122 in both Chrome and MS Edge and other Chromium-based browsers, just tested. The title is shown to the user when the format is not met (at least with the pattern validation) adding a descriptive message to the standard browser-text.Dali
M
53

It's very simple to control custom messages with the help of the HTML5 oninvalid event

Here is the code:

User ID 
<input id="UserID"  type="text" required 
       oninvalid="this.setCustomValidity('User ID is a must')">
Mariehamn answered 20/7, 2012 at 5:7 Comment(1)
it is required to set validity message to blank once control reveives input else first executed validity message will be displayed for all fields. Add oninput="setCustomValidity('')" whenever calling setCustomValidity(). +1 to Somnath's answer.Presbyterial
S
48

By setting and unsetting the setCustomValidity in the right time, the validation message will work flawlessly.

<input name="Username" required 
oninvalid="this.setCustomValidity('Username cannot be empty.')" 
onchange="this.setCustomValidity('')" type="text" />

I used onchange instead of oninput which is more general and occurs when the value is changed in any condition even through JavaScript.

Signalman answered 1/4, 2014 at 4:56 Comment(10)
This should be the accepted answer. Worked in Windows 10 1709 in the following browsers: Chrome 66.0.3359.139 64 bits, Firefox 59.0.3 (64-bit), Internet Explorer 11.431.16299.0, Edge 41.16299.402.0. Worked in macOS 10.13.2 in Safari 11.0 (13604.1.38.1.6). Worked in Android 4.4.4 in Chrome 66.0.3359.158. For those looking for the JSX way: onInvalid={(e) => { e.target.setCustomValidity('foo') }} onChange={(e) => { e.target.setCustomValidity('') }}.Intended
Addendum: e might be null, for example when an option is removed from a React Select field. Don't forget to check for that.Intended
Errata: to use this with React Select, you have to pass onChange and onInvalid as inputProps={{ onInvalid: …, onChange: …, }}Intended
Addendum: type='email' is a bit more difficult to treat, since using setCustomValidity sets customError: true to e.target.validity even if the input is valid. I'm trying to find a way to fix it.Intended
@Intended Did you figure this out?Chacma
@Chacma Regarding selects, no. Regarding emails, I think I did. I'll prepare a minimum working example tomorrow and post it here.Intended
@Chacma Email hereIntended
@Chacma Regarding selects, I remembered something: I took a very quick glance at React Forms and I think it might be able to replace native validation, but it will be a long time before I start working on this refactoring.Intended
This comment is no longer valid. Changing setCustomValidity works in Chrome as expected.Pithos
Using onchange instead of oninput makes this work correctly with HTML5 date type fields.Lid
F
43

I have made a small library to ease changing and translating the error messages. You can even change the texts by error type which is currently not available using title in Chrome or x-moz-errormessage in Firefox. Go check it out on GitHub, and give feedback.

It's used like:

<input type="email" required data-errormessage-value-missing="Please input something">

There's a demo available at jsFiddle.

Franny answered 2/4, 2012 at 13:8 Comment(3)
The jsFiddle does not work. I'd love for this solution to work but it's not inspiring confidence.Triennial
@Triennial you're right, I updated the jsFiddle and it's working again!Franny
Works like a charm! Thanks, this will surely help with localizing validation: can't believe that they didn't think about that when designing this.Triennial
F
36

Try this one, its better and tested:

    function InvalidMsg(textbox) {
        if (textbox.value === '') {
            textbox.setCustomValidity('Required email address');
        } else if (textbox.validity.typeMismatch){
            textbox.setCustomValidity('please enter a valid email address');
        } else {
           textbox.setCustomValidity('');
        }

        return true;
    }
<form id="myform">
    <input id="email" 
           oninvalid="InvalidMsg(this);" 
           oninput="InvalidMsg(this);"
           name="email"  
           type="email" 
           required="required" />
    <input type="submit" />
</form>

Demo:

http://jsfiddle.net/patelriki13/Sqq8e/

Fraser answered 14/3, 2014 at 2:7 Comment(2)
hey....nice solution but type=email doesnot work in safari browser . have a look w3schools.com/html/html_form_input_types.aspAssonance
Yes, it is not supported for safari, but I gave an answer for set custom validation message.Fraser
S
15

The easiest and cleanest way I've found is to use a data attribute to store your custom error. Test the node for validity and handle the error by using some custom html. enter image description here

le javascript

if(node.validity.patternMismatch)
        {
            message = node.dataset.patternError;
        }

and some super HTML5

<input type="text" id="city" name="city" data-pattern-error="Please use only letters for your city." pattern="[A-z ']*" required>
Sagacious answered 17/4, 2013 at 19:19 Comment(1)
Despite the error saying only letters, the pattern allows space and apostrophe? Also, A-z allows '[', '\', ']', '^', '_', and '`'.Pleochroism
M
9

The solution for preventing Google Chrome error messages on input each symbol:

<p>Click the 'Submit' button with empty input field and you will see the custom error message. Then put "-" sign in the same input field.</p>
<form method="post" action="#">
  <label for="text_number_1">Here you will see browser's error validation message on input:</label><br>
  <input id="test_number_1" type="number" min="0" required="true"
         oninput="this.setCustomValidity('')"
         oninvalid="this.setCustomValidity('This is my custom message.')"/>
  <input type="submit"/>
</form>

<form method="post" action="#">
  <p></p>
  <label for="text_number_1">Here you will see no error messages on input:</label><br>
  <input id="test_number_2" type="number" min="0" required="true"
         oninput="(function(e){e.setCustomValidity(''); return !e.validity.valid && e.setCustomValidity(' ')})(this)"
         oninvalid="this.setCustomValidity('This is my custom message.')"/>
  <input type="submit"/>
</form>
Megalo answered 19/2, 2019 at 20:11 Comment(1)
On first look, I thought I knew why this works, but I can't figure it out. Could somebody explain? (how does the IIFE change this?)Sculpsit
E
8

I have a simpler vanilla js only solution:

For checkboxes:

document.getElementById("id").oninvalid = function () {
    this.setCustomValidity(this.checked ? '' : 'My message');
};

For inputs:

document.getElementById("id").oninvalid = function () {
    this.setCustomValidity(this.value ? '' : 'My message');
};
Eloyelreath answered 18/8, 2016 at 12:57 Comment(0)
A
6

If your error message is a single one, then try below.

<input oninvalid="this.setCustomValidity('my error message')"
       oninput="this.setCustomValidity('')">  <!-- 👈 don't forget it. -->

To handle multiple errors, try below

<input oninput="this.setCustomValidity('')">
<script>
inputElem.addEventListener("invalid", ()=>{
    if (inputElem.validity.patternMismatch) {
      return inputElem.setCustomValidity('my error message')
    }
    return inputElem.setCustomValidity('') // default message
})
</script>

Example

You can test for input illegal filename or valueMissing.

<form>
<input pattern="[^\\/:\x22*?<>|]+"
       placeholder="input file name"       
       oninput="this.setCustomValidity('')"
       required
>
  <input type="submit">
</form>

<script>
  const form = document.querySelector("form")

  const inputElem = document.querySelector(`input`)

  inputElem.addEventListener("invalid", ()=>{   
    if (inputElem.validity.patternMismatch) {
      return inputElem.setCustomValidity('Illegal Filename Characters \\/:\x22*?<>|')
    }
    return inputElem.setCustomValidity('') // return default message according inputElem.validity.{badInput, customError, tooLong, valueMissing ...}
  })

  form.onsubmit = () => {
    return false
  }
</script>
Atronna answered 10/11, 2021 at 11:2 Comment(0)
S
4

Okay, oninvalid works well but it shows error even if user entered valid data. So I have used below to tackle it, hope it will work for you as well,

oninvalid="this.setCustomValidity('Your custom message.')" onkeyup="setCustomValidity('')"

Schlemiel answered 28/8, 2018 at 10:48 Comment(0)
I
2

Adapting Salar's answer to JSX and React, I noticed that React Select doesn't behave just like an <input/> field regarding validation. Apparently, several workarounds are needed to show only the custom message and to keep it from showing at inconvenient times.

I've raised an issue here, if it helps anything. Here is a CodeSandbox with a working example, and the most important code there is reproduced here:

Hello.js

import React, { Component } from "react";
import SelectValid from "./SelectValid";

export default class Hello extends Component {
  render() {
    return (
      <form>
        <SelectValid placeholder="this one is optional" />
        <SelectValid placeholder="this one is required" required />
        <input
          required
          defaultValue="foo"
          onChange={e => e.target.setCustomValidity("")}
          onInvalid={e => e.target.setCustomValidity("foo")}
        />
        <button>button</button>
      </form>
    );
  }
}

SelectValid.js

import React, { Component } from "react";
import Select from "react-select";
import "react-select/dist/react-select.css";

export default class SelectValid extends Component {
  render() {
    this.required = !this.props.required
      ? false
      : this.state && this.state.value ? false : true;
    let inputProps = undefined;
    let onInputChange = undefined;
    if (this.props.required) {
      inputProps = {
        onInvalid: e => e.target.setCustomValidity(this.required ? "foo" : "")
      };
      onInputChange = value => {
        this.selectComponent.input.input.setCustomValidity(
          value
            ? ""
            : this.required
              ? "foo"
              : this.selectComponent.props.value ? "" : "foo"
        );
        return value;
      };
    }
    return (
      <Select
        onChange={value => {
          this.required = !this.props.required ? false : value ? false : true;
          let state = this && this.state ? this.state : { value: null };
          state.value = value;
          this.setState(state);
          if (this.props.onChange) {
            this.props.onChange();
          }
        }}
        value={this && this.state ? this.state.value : null}
        options={[{ label: "yes", value: 1 }, { label: "no", value: 0 }]}
        placeholder={this.props.placeholder}
        required={this.required}
        clearable
        searchable
        inputProps={inputProps}
        ref={input => (this.selectComponent = input)}
        onInputChange={onInputChange}
      />
    );
  }
}
Intended answered 24/5, 2018 at 14:23 Comment(0)
C
1
const username= document.querySelector('#username');
const submit=document.querySelector('#submit');

submit.addEventListener('click',()=>{
    if(username.validity.typeMismatch){
        username.setCustomValidity('Please enter User Name');
    }else{
        username.setCustomValidity('');
    }
if(pass.validity.typeMismatch){
        pass.setCustomValidity('Please enter Password');
    }else{
        pass.setCustomValidity('');
    }

})
Cuttie answered 7/4, 2022 at 12:43 Comment(1)
This doesn't seem to add anything new to the existing answers.Cedar
A
1

For a totaly custom check logic:

$(document).ready(function() {

  $('#form').on('submit', function(e) {
    if ($('#customCheck').val() != 'apple') {
      $('#customCheck')[0].setCustomValidity('Custom error here! "apple" is the magic word');
      $('#customCheck')[0].reportValidity();
      e.preventDefault();
    }
  });

  $('#customCheck').on('input', function() {
    $('#customCheck')[0].setCustomValidity('');
  });


});
input {
  display: block;
  margin-top: 15px;
}

input[type="text"] {
  min-width: 250px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<form id="form">
  <input type="text" placeholder="dafault check with 'required' TAG" required/>
  <input type="text" placeholder="custom check for word 'apple'" id="customCheck" />
  <input type="submit">
</form>
Armpit answered 26/6, 2022 at 14:56 Comment(0)
D
1

A vanilla JavaScript answer. It is better to have event listeners in the scripts rather than inline HTML event listeners.

for(const input of document.querySelectorAll("input, select, textarea")){
    input.addEventListener("invalid", function(){
        input.setCustomValidity("This field cannot be left blank");
    });
}
Dour answered 9/1 at 15:20 Comment(0)
A
-15

Can be easily handled by just putting 'title' with the field:

<input type="text" id="username" required title="This field can not be empty"  />
Antonomasia answered 16/5, 2019 at 19:11 Comment(4)
This doesn't change the error message displayed. It only shows a title on the input when you hover.Cinder
This is not the correct usability of "title" to show some alert for the input field.Goosestep
'title' doesn't change the default 'required' message.Noonday
Title has a very different purpose altogether.Oshinski

© 2022 - 2024 — McMap. All rights reserved.