Validating a form when either a "select" OR "input" is required - javascript

I have a page containing multiple forms, all different, and when one is submitted I use the function below to gather all the inputs from that form with the class "required" and check for empty values:
function validateForm(form) {
var inputs = form.getElementsByTagName('input');
var selects = form.getElementsByTagName('select');
var errors = 0;
for (var i = 0; i < inputs.length; i++) {
if(inputs[i].classList.contains('required')) {
if(inputs[i].value === "") {
inputs[i].classList.add("warning");
errors++;
} else {
inputs[i].classList.remove("warning");
}
}
}
if(errors) {
return false;
} else {
return true;
}
}
If it finds an empty value, it adds the class "warning" which just gives the input a red border, then returns false so the form doesn't get submitted.
Here's where I'm running into trouble: Some forms contain a <select> and a text input, ONE of which must be filled in, but not both, as well as various other text inputs. I'm trying to figure out how to modify the above function to handle this.
Let's say the form is for adding a new product. The select is dynamically populated with existing product "categories" and the text input is for if the user wants to create a new category. Here's a simplified version of the form:
<form method = "post" onsubmit = "return validateForm(this)">
<div class = "form-group">
<label>Product Name</label>
<input class = "form-control required" type = "text" name = "product" />
</div>
<div class = "form-group">
<select class = "form-control required" id = "category" name = "category[]">
<option value = "">Select Existing Category</option>
<option value = "Shirts">Shirts</option>
<option value = "Shoes">Shoes</option>
<option value = "Pants">Pants</option>
</select>
</div>
<div class = "form-group">
<label>Create New Category</label>
<input class = "form-control required" type = "text" name = "category[]" />
</div>
<div class = "form-group">
<input class = "btn btn-primary" type = "submit" value = "Submit" />
</div>
</form>
Since I'm using a for loop to go through the inputs - the select and the input are not going to have the same index, so I can't do something like this:
if((selects[i].value === "" && inputs[i].value === "") || (selects[i].value !== "" && inputs[i].value !== "")) {
// add the warning class to both
}
I feel the answer lies somewhere in using the name attribute, i.e. compare selects.name and inputs.name, but how do I get around the differing index in the loop? And also, it should only make this comparison when the select is encountered anyway. It doesn't necessarily exist, depending on the form.
Basically, I need to modify my function to do this:
I. Gather all inputs and selects (if any - some forms will not) from a submitted form
II. Make sure none of the inputs with the "required" class are blank (unless there's a corresponding select, in which case see III below)
III. If there's a select, find the text input with the same "name" (not a requirement to have the same name, but I assume this is the right way to do it). One of them, but not both, must have a value. If both are blank, or both have a value, they should get the "warning" class;
Any help anyone can offer will be greatly appreciated!

Here's a function that do exactly what you want and can handle any form you want, as long as they have the same HTML structure.
Notes:
I recommend avoiding inline event listeners as much as you can, in
the snippet below I used addEventListener method to attach submit
event to all the forms in the document, you can change this to just
some specific forms if you want.
Instead of only adding a border to the required elements, I suggest
you also add some text to tell what the problem is.
// getting all forms in the page you can also get specific forms based on their class-name
var forms = document.getElementsByTagName('form'),
l = forms.length,
i = 0;
// adding submit submit event listener to the referenced forms
for(; i < l; i++) {
forms[i].addEventListener('submit', validateForm);
}
function validateForm(e) {
var els = this.querySelectorAll('input.required'),
len = els.length,
err = false,
c = 0,
inpName = '';
// checking if the form has a select, if so, allow only the select or the input to be filled
var isSelect = this.getElementsByTagName('select');
if(isSelect[0] !== undefined && isSelect[0] !== null) {
var inp = isSelect[0].parentNode.nextElementSibling.querySelector('input.required');
inpName = inp.name;
if((isSelect[0].value == '' && inp.value.trim().length === 0) || (isSelect[0].value != '' && inp.value.trim().length > 0)) {
err = true;
isSelect[0].classList.add("warning");
inp.classList.add("warning");
} else {
isSelect[0].classList.remove("warning");
inp.classList.remove("warning");
}
}
// iterate through the rest of the inputs and check for empty one, thus trimming them before checking
for(; c < len; c++) {
if(els[c].name !== inpName) {
if(els[c].value.trim() == '') {
err = true;
els[c].classList.add("warning");
} else {
els[c].classList.remove("warning");
}
}
}
// based on the error variable, either submit the form or cancel submission
(!err) ? this.submit():e.preventDefault();
}
.warning {
border: 2px solid red;
}
<form method="post">
<div class="form-group">
<label>Product Name</label>
<input class="form-control required" type="text" name="product" />
</div>
<div class="form-group">
<select class="form-control required" id="category" name="category[]">
<option value="">Select Existing Category</option>
<option value="Shirts">Shirts</option>
<option value="Shoes">Shoes</option>
<option value="Pants">Pants</option>
</select>
</div>
<div class="form-group">
<label>Create New Category</label>
<input class="form-control required" type="text" name="category[]" />
</div>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
Hope I pushed you further.
You may get a message saying: "The custom error module does not
recognize this error." when you successfully submit the form from the
snippet above, that due to StackOverflow's restrictions as they
don't allow/server side code (StackOverflow doesn't let the form to
be submitted).

Related

How to validate all inputs which exists on page?

All inputs from page
I have this html page which is dynamical created, which contains some divs. Every div-question(0,1,2, etc) contain an input based on which answer type the user chose. I want to validate every single inputs from page and:
If value of one input type number,text,date is != "" alert("something")
else send the value in an array;
If checkbox/radio is not checked alert("something");
I tried something like this:
let nrDiv = document.getElementsByClassName("div-question");
let existInput = nrDiv[0].querySelector("input[type='text']");
let numberInput = nrDiv[0].querySelector("input[type='number']");
if (document.body.contains(existInput)) {
for (let i=0; i < nrDiv.length ;i++) {
let container = document.getElementsByClassName("div-questions" + i + "");
let userInputAnswer = container[0].querySelector("input[type='text']");
if (userInputAnswer.value == "") {
alert("Adaugati un raspuns!")
return;
}
if (userInputAnswer.value != ""){
let answer = {
question: questions[i].textQuestion,
answer: userInputAnswer.value
}
answers.push(answer);
}
}
}
It's working but if I come with another for loop, for input type="number" is not working anymore. I'm getting value null. So if I come with this:
if (document.body.contains(numberInput)) {
for (let i=0; i < nrDiv.length ;i++) {
let container = document.getElementsByClassName("div-questions" + i + "");
let userInputAnswer = container.querySelector("input[type='number']");
if (userInputAnswer.value == "") {
alert("Adaugati un raspuns!")
return;
}
if (userInputAnswer.value != ""){
let answer = {
question: questions[i].textQuestion,
answer: userInputAnswer.value
}
answers.push(answer);
}
}
}
And for the checkbox and radio inputs I don't have any idea. I want something like this:
If all inputs are not empty and minimum one checkbox/radio is checked, send the answer and question in an array else alert("error");
I feel like this is simple once you add the required attribute and/or a pattern.
This is a simple POC:
<form action="">
<input type="text" required>
<input type="number" required>
<input type="checkbox" required>
<input type="radio" required>
<input type="date" required>
<button type="submit">Submit</button>
</form>
Notice that when you click the submit button, it does the verification you want, in this case != "".

Validate a form with multiple elements?

I have a form that has multiple elements/types
inputs for name, email, address, etc.
radio button for shipping speed.
select tags for "state" & "credit card type".
I want to disable the submit button until the:
1.inputs are filled out.
the select tags have an option selected
the radio is checked.
I've selected the elements (see below);
const btn = document.querySelector('#olegnax-osc-place-order-button');
let inputs = document.querySelectorAll('#olegnax-osc-billing-address-list .required-entry, input#authorizenet_cc_number');
let selectTags = document.querySelectorAll('#olegnax-osc-billing-address-list select, #payment_form_authorizenet select');
let radio = document.querySelector('#s_method_owebiashipping1_id_06');
My question is, being the form consists of 3 different types (input, select, radio), can I just create one array with all of these elements and loop though to make sure the value for each is not blank?
For example, say I store all the different elements in an array called "requiredFields" would this work?:
for (var i = 0; i < requiredFeilds.length; i++) {
if (requiredFeilds[i].value === '') {
btn.disabled = true;
}else {
btn.disabled = false;
}
}
There's a lot more to form validation than meets the eye, but that being said you have a major flaw in your logic. Namely, as you loop over all the fields, you could be changing btn.disabled back and forth depending on the value of the form field (or lack of a value).
Instead, begin with the button disabled, and then instead of looping, use Array.prototype.some (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) to check if any field is missing a value, something such as:
btn.disabled = requiredFields.some(field => field === '');
There's lots else to address with regards to your approach but this corrects your current logic error and is much more concise.
You can loop through everything but the radio buttons easily. For the radio buttons, you want to check if any of the buttons in a radio group are checked, so it is a little more complicated. It might be easier to just designate a radio button as default, with the checked attribute:
document.querySelector("input[type=submit]").disabled = true;
const inputs = Array.prototype.slice.call(document.forms["form1"].querySelectorAll("input[type=text], select"));
document.forms["form1"].addEventListener("input", () => {
let complete = true;
inputs.forEach((field) => {
if(field.value.trim() === "") {
complete = false;
}
});
document.querySelector("input[type=submit]").disabled = !complete;
});
form{
display:flex;
flex-flow:column;
align-items:flex-start;
}
<form name="form1" id="form1">
<input type="text" name="bar" />
<input type="text" name="foo" />
<select name="biz">
<option disabled selected value>---</option>
<option>One</option>
<option>Two</option>
</select>
<select name="baz">
<option disabled selected value>---</option>
<option>One</option>
<option>Two</option>
</select>
<label>
A <input type="radio" name="buzz" value="a" checked />
</label>
<label>
B <input type="radio" name="buzz" value="b" />
</label>
<input type="submit" value="Submit" />
</form>
Yes, just combine your selectors and use a comma, and check tagName:
const requiredFields = document.querySelectorAll( "
#olegnax-osc-place-order-button,
#olegnax-osc-billing-address-list .required-entry,
input#authorizenet_cc_number,
#olegnax-osc-billing-address-list select,
#payment_form_authorizenet select,
#s_method_owebiashipping1_id_06
" );
for( let input of requiredFields ) {
if( input.tagName == "INPUT" ) {
}
else if( input.tagName == "SELECT" ) {
}
else if( input.tagName == "TEXTAREA" ) {
}
}
You should also use the required attribute too:
<input type="text" required />
<select required></select>
<textarea required></textarea>

How to validate a simple web form

Im working on a small form and using js to validate if the fields are empty or not. I have a span class next to the name field "name" "email".
For the "name" field, i have a span class called "error".
For the "email" field, i have another span class called "error2".
what can i do to only use one class to display the "error message", because of course i will have more field and I don't want to keep adding more classes. error3, error4
HTML:
<form action="#i" name="myForm" onsubmit="return(validate());">
Name: <span id="error"></span><br>
<input type="text" name="Name" /><br><br>
EMail: <span id="error2"></span><br>
<input type="text" name="EMail" /><br> <br>
<input type="submit" value="Submit" /> <br>
</form>
JS:
function validate()
{
var t = 0;
if( document.myForm.Name.value == "" )
{
document.getElementById('error').innerHTML = "<br>Empty";
t = 1;
}
if( document.myForm.EMail.value == "" )
{
document.getElementById('error2').innerHTML = "<br>Empty";
t = 1;
}
if(t == 1)
{
return false;
}
else
return true;
}
Instead of giving the spans the attribute of Id, use classes instead. So for example, you can define ALL your spans as follows:
<span class="error"> ... </span>
Then, in your validate function, you can obtain these spans through:
document.getElementsByClassName('error');
Keep in mind though, this returns an array, which would actually be perfect for your function. This way, you can write a basic for-loop to go through each span and make sure each field is filled in correctly.

Showing hidden form field when specific URL is passed

I have a form that is populated dynamically using URL parameters. The problem is that one of the parameters in the URL selects an option in a dropdown, which should then show a field. That field is not showing on page load.
Note that I have a simple check on page load that should show the field, so I am wondering if URL parameters are passed after the document.ready state.
Here is the URL that I am using:
https://donate.globaltc.org/#designation=3&missionary=Jerry%20Nance
The dropdown is selecting the correct option.
Here is my JS:
// Show or hide field
var $missionary = $('.missionary');
var $designation = $('#designation');
var $project = $('.project');
if($designation.val() != 3){
$missionary.hide();
} else {
$missionary.show();
}
// Handle URL parameters
var hashParams = window.location.hash.substr(1).split('&'); // substr(1) to remove the `#`
for(var i = 0; i < hashParams.length; i++){
var p = hashParams[i].split('=');
document.getElementById(p[0]).value = decodeURIComponent(p[1]);
}
HTML Form:
<div class="form-group col-sm-6">
<label for="exampleInputEmail1">How would you like this gift to be designated?</label>
<select class="form-control" id="designation" name="designation">
<option value="1" selected="selected">Most Urgent Need</option>
<option value="3">A Global Team Member</option>
</select>
</div>
<div class="form-group col-sm-6 missionary">
<label for="missionary">Name of Team Member</label>
<input type="text" class="form-control" id="missionary" name="missionary">
</div>
I found an error in my code, but it did not solve my issue. The error:
if($designation.val() != 3){ //Should be string not int
Here is the hacky way that I made this work.
setTimeout(function(){
if($designation.val() == '3'){
$missionary.show();
} else {
$missionary.hide();
}
}, 100);
I would still like a more definitive answer on why this is happening.
You can use var designationValue = parseInt($designation.val(), 10); to figure out what the int value of the field is before using it in your if statement.
Documentation for parseInt

inline javascript form validation

I'm working on a form validation script at work and am having some difficulty. The form is meant to make sure that the user fills out a name, a real-looking email, a category (fromt he drop down) and a question:
This names the form and gathers all the data up from the form:
<script>
function checkForm(form1) {
name = document.getElementById("FieldData0").value;
category = document.getElementById("FieldData3").value;
question = document.getElementById("FieldData1").value;
email = document.getElementById("FieldData2").value;
This checks to see that something is in the "name" field. It works fine and validates exactly like it should, displaying the error text:
if (name == "") {
hideAllErrors();
document.getElementById("nameError").style.display = "inline";
document.getElementById("FieldData0").select();
document.getElementById("FieldData0").focus();
return false;
This also works just like it should. It checks to see if the email field is empty and if it is empty,displays error text and selects that field:
} else if (email == "") {
hideAllErrors();
document.getElementById("emailError").style.display = "inline";
document.getElementById("FieldData2").select();
document.getElementById("FieldData2").focus();
return false;
}
This also works just like it should, makes sure that the questions field isn't empty:
else if (question == "") {
hideAllErrors();
document.getElementById("questionError").style.display = "inline";
document.getElementById("FieldData1").select();
document.getElementById("FieldData1").focus();
return false;
}
This one works partially - If no drop down is selected, it will display the error message, but that doesn't stop the form from submitting, it just displays the error text while the form submits:
else if (category == "") {
hideAllErrors();
document.getElementById("categoryError").style.display = "inline";
document.getElementById("FieldData3").select();
document.getElementById("FieldData3").focus();
return false;
}
This one doesn't work at all no matter where I put it. I used a variation on the same script last week and it worked fine. This is supposed to check to see that the email entered looks like a real email address:
else if (!check_email(document.getElementById("FieldData1").value)) {
hideAllErrors();
document.getElementById("emailError2").style.display = "inline";
document.getElementById("FieldData2").select();
document.getElementById("FieldData2").focus();
return false;
}
Otherwise it lets the form submit:
return true;
}
This checks the email out:
function check_email(e) {
ok = "1234567890qwertyuiop[]asdfghjklzxcvbnm.#-_QWERTYUIOPASDFGHJKLZXCVBNM";
for(i=0; i < e.length ;i++){
if(ok.indexOf(e.charAt(i))<0){
return (false);
}
}
if (document.images) {
re = /(#.*#)|(\.\.)|(^\.)|(^#)|(#$)|(\.$)|(#\.)/;
re_two = /^.+\#(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
if (!e.match(re) && e.match(re_two)) {
return (-1);
}
}
}
This function hides all errors so the user isn't bombarded with red text. I tried putting in "document.getElementById("emailError").style.display = "none"" but that breaks the whole thing:
function hideAllErrors() {
document.getElementById("nameError").style.display = "none"
document.getElementById("emailError").style.display = "none"
document.getElementById("categoryError").style.display = "none"
document.getElementById("questionError").style.display = "none"
}
</script>
And the form looks like this:
<form onSubmit="return checkForm();" method="post" action="http://www.emailmeform.com/fid.php?formid=303341io4u" name="form1">
<p><div class=error id=nameError>Required: Please enter your name<br/></div><p><strong>Name:</strong> <span></span><br><input type="text" name="FieldData0" id="FieldData0" value="" size="22" tabindex="1" />
<label for="name"></label></p>
<p><div class=error id=emailError>Required: Please enter your email address<br/></div>
<div class=error id=nameError2>This doesn't look like a real email address, please check and reenter<br/></div>
<strong><p>Email:</strong> <span>(will not be published)</span><br><input type="text" name="FieldData2" id="FieldData2" value="" size="22" tabindex="2" />
<label for="email"></label>
</p>
<div class=error id=categoryError>Please select a category from the drop-down menu<br></div>
<p><strong>Category:</strong> <span></span><br>
<p><select id="FieldData3" name="FieldData3">
<option value="">Please select a category</option>
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
<option value="d">d</option>
<option value="e">e</option>
<option value="f">f</option>
<option value="other">Other</option>
</select><label for="category"></label>
<p><div class=error id=questionError>Please type your question in the box below:<br></div><label for="question"><strong><p>Your Question:</strong> <span></span></label><br>
<textarea name="FieldData1" id="FieldData1" cols="50" rows="10"></textarea></p>
<p><input type="submit" class="btn" value="Submit Question" name="Submit"></p>
</div>
</form>
Is the problem the order that I run the checks? I can't seem to figure this out. Any help would be appreciated.
I've taken the liberty to re-write your javascript to make it more readable and easier to debug.
As Marc Bernier mentioned, the dropdown element does not support the select function so I put an if statement around it to prevent an exception. I've also simplified your checkEmail function, it seemed rather convoluted. I renamed it to isAnInvalidEmail in order to make the checkForm code simpler.
You have also incorrectly named the 'emailError2' div in your HTML, which would cause another exception in the javascript. Your HTML is rather messy and, in some cases, invalid. There are missing quotes on some attribute values and missing end-tags. You should consider using the W3C validator to ensure your HTML is clean and is standards compliant.
I've hosted your code on jsbin: http://jsbin.com/iyeco (editable via http://jsbin.com/iyeco/edit)
Here's the cleaned up Javascript:
function checkForm() {
hideAllErrors();
var formIsValid =
showErrorAndFocusIf('FieldData0', isEmpty, 'nameError')
&& showErrorAndFocusIf('FieldData2', isEmpty, 'emailError')
&& showErrorAndFocusIf('FieldData2', isAnInvalidEmail, 'emailError2')
&& showErrorAndFocusIf('FieldData3', isEmpty, 'categoryError')
&& showErrorAndFocusIf('FieldData1', isEmpty, 'questionError');
/* For debugging, lets prevent the form from submitting. */
if (formIsValid) {
alert("Valid form!");
return false;
}
return formIsValid;
}
function showErrorAndFocusIf(fieldId, predicate, errorId) {
var field = document.getElementById(fieldId);
if (predicate(field)) {
document.getElementById(errorId).style.display = 'inline';
if (field.select) {
field.select();
}
field.focus();
return false;
}
return true;
}
function isEmpty(field) {
return field.value == '';
}
function isAnInvalidEmail(field) {
var email = field.value;
var ok = "1234567890qwertyuiop[]asdfghjklzxcvbnm.#-_QWERTYUIOPASDFGHJKLZXCVBNM";
for(i = 0; i < email.length; i++){
if(ok.indexOf(email.charAt(i)) < 0) {
return true;
}
}
re = /(#.*#)|(\.\.)|(^\.)|(^#)|(#$)|(\.$)|(#\.)/;
re_two = /^.+\#(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
return re.test(email) || !re_two.test(email);
}
function hideAllErrors() {
document.getElementById("nameError").style.display = "none"
document.getElementById("emailError").style.display = "none"
document.getElementById("emailError2").style.display = "none"
document.getElementById("categoryError").style.display = "none"
document.getElementById("questionError").style.display = "none"
}
And the cleaned up HTML:
<form onSubmit="return checkForm();" method="post" action="http://www.emailmeform.com/fid.php?formid=303341io4u" name="form1">
<div>
<div class="error" id="nameError">
Required: Please enter your name
</div>
<label for="FieldData0"><strong>Name:</strong></label>
<input type="text" name="FieldData0" id="FieldData0" value="" size="22" tabindex="1" />
</div>
<div>
<div class="error" id="emailError">
Required: Please enter your email address
</div>
<div class="error" id="emailError2">
This doesn't look like a real email address, please check and reenter
</div>
<label for="FieldData2"><strong>Email:</strong>(will not be published)</label>
<input type="text" name="FieldData2" id="FieldData2" value="" size="22" tabindex="2" />
</div>
<div>
<div class="error" id="categoryError">
Please select a category from the drop-down menu
</div>
<label for="FieldData3"><strong>Category:</strong></label>
<select id="FieldData3" name="FieldData3">
<option value="">Please select a category</option>
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
<option value="d">d</option>
<option value="e">e</option>
<option value="f">f</option>
<option value="other">Other</option>
</select>
</div>
<div>
<div class="error" id="questionError">
Please type your question in the box below:
</div>
<label for="FieldData1"><strong>Your Question:</strong></label>
<textarea name="FieldData1" id="FieldData1" cols="50" rows="10"></textarea>
</div>
<input type="submit" class="btn" value="Submit Question" name="Submit">
</form>
Regarding the error on the drop-down, don't call this line:
document.getElementById("FieldData1").select();
I seem to recall having the exact same problem a few weeks ago.
First problem: move the content of that if statement into a function...then go from there. You have about 5 pieces of code doing essentially the same thing.
Next: since you're only allowing one error message at a time, create a generic div to hold it and just move the thing. That way, you don't need to keep track of hiding certain errors, displaying others, etc.
Next: only return true or false from your check_email function...returning -1 and false, etc. is bad form even though javascript is lenient on such things.
After you have cleaned up your code, it will be much easier to debug.
I would recommend getting rid of the whole if else chain and check each on individually this this.
var error = 0;
if (value == '') {
error = 1;
// other stuff;
}
if (value2 == '') {
error = 1;
// do stuff;
}
...
if (error) {
// show error
} else {
// submit form
}
Try replacing the == for === which doesn't type cast. It might help you with the dropdown problem.
Your function is returning false and it might also return -1.
As I don't know what type cast JavaScript does with !-1 you should also do this:
check_email(...)!==false;
Instead of this:
!check_email(...)

Categories

Resources