Stopping Form from submitting if not validated - javascript

I have a form with weights, and I am making sure that each weight is:
A number
Every weight > 0
Sum of all weights = 100.
The below example is just testing 1 and 3:
function assertWeightValidity() {
let weightSum = 0;
$.each($('[name^=weight_]'), function()
{
chai.assert(isNaN(this.value) === false, 'Weights should be numbers!');
let currWeight = Number(this.value);
console.log(currWeight);
weightSum += currWeight;
<!-- assert nothing is zero -->
});
console.log(weightSum);
}
onEnterCalculate("#calculate", assertWeightValidity);
and then I have onEnterCalculate function defined as:
function onEnterCalculate(selector, assertion) {
middleware = assertion || null;
document.onkeydown = function (evt) {
var keyCode = evt ? (evt.which ? evt.which : evt.keyCode) : event.keyCode;
if (keyCode == 13) {
if(middleware) { middleware(); }
$(selector).click();
}
}
}
I am a bit of a newbie in JavaScript. I have googled around but I cannot find the solution. What I am trying to achieve is, if chai at any point throws an Error, I do not want to submit the form, I want to alert the user and let them amend what they already inputted in the form. I found that something like that can be achieved with the preventDefault() call. Not sure how to grab the event inside of assertWeightValidity() (because presumable I would need to generate the event when chai throws an error). What currently happens is that chai throws an Uncaught Exception if the weight is bad, for example 'sadasda' but it conitnues and posts the form anyway.
Thanks

In a nutshell, you give your form a submit event where you run your validations. If you call e.preventDefault() and/or return false in that function, it won't submit. Otherwise, it will.
document.querySelector('form').addEventListener('submit', e => {
console.log('preventing submit');
e.preventDefault();
});
<form action="#">
<button type="submit">Submit</button>
</form>
From there, it is just a matter of validating your code however you want and then calling (or not calling) preventDefault() as necessary:
document.querySelector('form').addEventListener('submit', e => {
if (!document.querySelector('input').value) {
console.log('Must add a value');
e.preventDefault();
}
});
<form action="#">
<label>Value: <input /></label>
<button type="submit">Submit</button>
</form>
If you have multiple validation functions that all return a boolean (or better yet, an error message), an easy way to check would be to put them in an array and then use every() or filter() to see if they are all good.
const checkField = id => !!document.querySelector(`#${id}`).value;
document.querySelector('form').addEventListener('submit', e => {
if (![checkField('a'), checkField('b'), checkField('c')].every(Boolean)) {
console.log('All fields must have a value');
e.preventDefault();
}
});
<form action="#">
<label>Value: <input id="a"/></label>
<label>Value: <input id="b"/></label>
<label>Value: <input id="c"/></label>
<button type="submit">Submit</button>
</form>
Even better, it can return an error message if it doesn't then you can gather up the error messages with filter():
const checkField = id => document.querySelector(`#${id}`).value ? undefined : `Field "${id}" must have a value`;
document.querySelector('form').addEventListener('submit', e => {
const errors = [checkField('a'), checkField('b'), checkField('c')].filter(Boolean);
if (errors.length) {
console.log(errors);
e.preventDefault();
}
});
<form action="#">
<label>Value: <input id="a"/></label>
<label>Value: <input id="b"/></label>
<label>Value: <input id="c"/></label>
<button type="submit">Submit</button>
</form>
Finally, since you mentioned throwing errors, if you want it to actually throw errors, you can try catch them and then output those.
const checkField = id => {
if (!document.querySelector(`#${id}`).value) {
throw new Error(`Field ${id} must have a value`);
}
};
document.querySelector('form').addEventListener('submit', e => {
try {
checkField('a');
checkField('b');
checkField('c');
} catch (ex) {
console.log(ex.message);
e.preventDefault();
}
});
<form action="#">
<label>Value: <input id="a"/></label>
<label>Value: <input id="b"/></label>
<label>Value: <input id="c"/></label>
<button type="submit">Submit</button>
</form>
The downside of this though is you can't check multiple things at the same time, since it'll abort on the first error.

What you want is instead of listening to keyboard events, you should simply check the submit event fired by the form. It's a very convenient catch all, because any user interaction that triggers form submission (could be the enter key, clicking a submit button, or whatsoever) will be caught by this handler.
Since you did not tag your question with "jQuery" and you seem to be familiar with ES6 syntax, I have formulated my answer using the following assumptions. To get the result you want, it is quite simple:
Listen to the form's submit event
Refactor your assertWeightValidity() method so that it throws an error, which we can catch in the submit event handler
Call assertWeightValidity() in a try block
If no errors are thrown, we can then submit the form :)
More details for the assertWeightValidity() method: you need to (1) first check if the weight input elements have non-empty values that can be parsed into numbers, and (2) you also want to check the sum of these values if they match 100.
Use Array.map() to iterate through all your input elements and retrieve their value. Before returning, you can already implement the logic to check if they are numbers. When returning, make sure to use the + operator to coerce the values to numbers (HTML values are always returned as string!)
Use Array.reduce() to sum up the array of weights you have
Check if the sum matches 100.
A proof-of-concept example is as follow. Test case:
Leave any of the field blank. Should throw an error because one or more fields cannot be converted into a number
Use 1, 2, 3, 4 in all 4 input fields. Should throw an error because they don't sum to 100
Use 25 for each field, that will sum to 100 and you should see a console log informing you that the form is valid and will be submitted.
Note that since I don't know what chai is about, I have simply commented the line out:
const customForm = document.getElementById('customForm');
customForm.addEventListener('submit', function(e) {
// Intercept default form submission
e.preventDefault();
// Assert weight validity
try {
assertWeightValidity.call(this)
} catch (error) {
console.warn(error);
return;
}
// Programmatically trigger form submission if no errors are thrown
console.log('Submitting form now');
// Uncomment the next line to actually submit the form
// this.submit();
});
// Returns if the weights are valid or not
function assertWeightValidity() {
// Get array of all weights
const weightElements = Array.prototype.slice.call(this.querySelectorAll('input[name^=weight_]'));
// Use `Array.map` to check if numbers can be parsed and return an array of weights
const weightsArray = weightElements.map((weightElement) => {
// If weight is empty or not a number, we throw an error and exit
// chai.assert(!isNaN(weight.value), 'Weights should be numbers!');
if (weightElement.value === '' || isNaN(weightElement.value))
throw 'Weights should be numbers';
// Otherwise, we return the value
return +weightElement.value;
});
// Use `Array.reduce` to get the sum of weights
const totalWeight = weightsArray.reduce((weight, accumulatedWeight) => weight + accumulatedWeight);
if (totalWeight !== 100)
throw 'Weights do not add up to 100';
}
input {
display: block;
}
<form id="customForm">
<input type="number" name="weight_1" placeholder="Enter a value for weight 1" />
<input type="number" name="weight_2" placeholder="Enter a value for weight 2" />
<input type="number" name="weight_3" placeholder="Enter a value for weight 3" />
<input type="number" name="weight_4" placeholder="Enter a value for weight 4" />
<button id="calculate">Calculate</button>
</form>

Related

How can i check if there's nothing type on the search bar and if it a number return nothing?

How can I check if the user types nothing in the search bar and if its a number return nothing. And also, how do I clear the user recent value? I've manage to clear the value, but after the conditional statement using the same input.value = '', i receive an error.
form.addEventListener('submit', e => {
e.preventDefault();
input.value = ''; // I'm assuming this get overwritten;
if(input.value === '') {
console.error('Please type anything in the search bar');
} else {
return recipes();
}
});
So from the code above, when the user types in nothing it will spit our error in the console, and if users were to type in both A-Z and 0-9 the recipes will still load.
I've left with when users type in numbers it will spit out error in console and lastly, to clear user input value after typing.
This is a practice for my beginner portfolio.
First you don't need to set input.value to null, it'll be setup to the default value into the HTML tag.
Also, this depend on how you define your input variable, if it's common like the following example, it's will work fine with input.value
let form = document.getElementsByTagName('form')[0]
let input = document.querySelector('[type=text]')
form.addEventListener('submit', e => {
e.preventDefault();
if(input.value === '') {
console.error('Please type anything in the search bar');
} else {
console.log(`Submitted value: ${input.value}`) // Get directly from the input
}
});
// Another example:
input.addEventListener('input', e => {
// Easy way to get the value of the element who trigger the current `e` event
console.log(`Input updated: ${e.currentTarget.value}`)
});
<form>
<input type="text"/>
<input type="submit"/>
</form>

Why is setCustomValidity('') ignored on input (Chrome 65)

Note: to the best of my knowledge this question is not a duplicate question of the following:
HTML5: Why does my “oninvalid” attribute let the pattern fail?
HTML5 form required attribute. Set custom validation message?
How can I change or remove HTML5 form validation default error messages?
Overview
Given a field that:
Has pattern attribute set for validation, for example "[a-f,0-9]{4}" for a 4 character hex string input.
Has oninvalid set with setCustomValidity('...some message...') to define a custom validation message
Has oninput set with setCustomValidity('') to reset on input
Here is an example showing this:
/* jshint esnext: true */
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = field.value;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
event.target.setCustomValidity('must be valid 4 hex characters');
}
field.oninput = (event) => {
console.log("INPUT");
event.target.setCustomValidity('');
}
Output: <span id="output">No output</span>
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" autocomplete=off>
</form>
Validation works almost as desired, except when the user enters an invalid entry and then proceeds to try and edit it, where their following input states are still invalid:
At this point, neither the custom setCustomValidity message defined in oninvalid is used, nor the empty one defined in onInput.
Instead, as long as the field is in an invalid state and not blurred, the default Please match the requested format. message appears.
Question
What is going on here? Looking at the console, the oninput event is called each time, and therefore event.target.setCustomValidity(''); is called each time.
So why is it that we are still seeing the generic default validation message? Shouldn't setCustomValidity('') disable that?
An acceptable answer here should exhibit the following:
The parameter field is respected for validation.
Any validation message appears if and only if the user attempts to submit an invalid field and not when they modify the input immediately afterward.
The default Please match the requested format. message never appears at all.
It appears that this is a bug with Chrome 65 in windows.
using setCustomValidity('') in oninput should disable the default validation messages appearing on input.
The following workaround works for me:
/* jshint esnext: true */
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
const pattern = field.getAttribute("pattern");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = `User submitted: ${field.value}`;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
event.target.setCustomValidity('must be valid 4 hex characters');
}
field.oninput = (event) => {
console.log("INPUT");
event.target.setCustomValidity('');
event.target.removeAttribute("pattern");
}
field.onchange = (event) => {
console.log("CHANGE");
event.target.setAttribute("pattern", pattern);
}
Output: <span id="output">No output</span>
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" autocomplete=off>
</form>
setCustomValidity is meant to be used when multiple inputs, in combination are invalid. That is why it has to be reset to the empty string manually after. Other wise the title attribute should be used.
Trying to hide the validation error after editing the input is understandable but it is against the HTML5 form philosophy. It is meant to be shown as long as the input is invalid.
Adding maxlength can help the user to not cross the upper limit.
If you really want your bullet points to be satisfied feel free to not use HTML5 form validation but something custom instead.
So the reason a tooltip is shown even when setCustomValidity is set to empty string is because the input element is still invalid as per pattern attribute.
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" maxlength="4" minlength="4" autocomplete="off" title="must be valid 4 hex characters">
</form>
JS
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = field.value;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
}
field.oninput = (event) => {
console.log("INPUT");
}

Why isnt my javascript function being run?

I'm trying to create a log-in page that validates data before it gets submitted to my php page that handles it. I'm using javascript to validate. This is my code:
<div class = mainInfo>
<?php include "header.php"; ?>
<form name = SignUpForm action = signUpHandler.php method ='POST' class = inputLists>
username: <input type = text name = "userName">
password: <input id= "p1" type = password name = "password">
reenter password: <input id ="p2" type = password name = "passwordConfirmation">
<input type="submit" name =" submitButton" value ="submit">
</form>
<div id="feedback">
</div>
</div>
<script>
function validate()
{
document.getElementById("feedback").innerHTML = "functionbeingcalled";
var p1 = document.getElementById("p1").value,
p2 = document.getElementById("p2").value);
if( ! p1===p2 )
{
document.getElementById("feedback").innerHTML = "passwords dont match";
}
if(p1==="")
{
document.getElementById("feedback").innerHTML = "Must have a password";
}
}
window.setInterval(validate(),1000);
</script>
<?php include "footer.php"; ?>
I would've thought that this script should run every second from the time that the page loads, but the script isn't being run at all. This line:
document.getElementById("feedback").innerHTML = "functionbeingcalled";
isn't working either.
Besides for this question, is it possible to validate data before submitting using only php? I'm new to web programming.
Pass the function instead of calling it.
// no parentheses!
window.setInterval(validate, 1000);
And this is wrong.
if( ! p1===p2 )
it should be this
if( p1!==p2 )
because of the higher precedence of the prefix !
I would suggest that you add listeners on your input fields! ;)
It will then only run the validation code when changes are made. In other words; only when necessary.
It will run the validation code "immediately" when input is changes. Instead of validation every 1000 ms.
I see you are not using jQuery (yet)? If you want to validate on 'change' using plain js, here is a solution: Plain js solution
If you are okay with adding the jQuery library to you code, then it can be done very easy like this jQuery solution
Well, you've got several issues...
First, with setInterval(), you only pass a reference to the function that should be called (validate in your case), you don't actually invoke it as you are doing (validate()). This essentially runs validate immediately and then sets the return value from it as the function to be called every second. Since validate() doesn't return a value, nothing happens every second thereafter.
You also have a typo with: if( ! p1===p2 ), which indicates that the Boolean opposite of p1 is being tested against p2. What you want is: if(p1 !== p2 ), which is how you express "not strictly equal to".
Now, really you are going about validation the wrong way. Instead of running a validation function on a timer, which is inefficient, you'd want to validate in one or more of these cases:
just before the entire form is submitted
just after the user leaves a form field
as the user is entering data
some combination of all 3
Each of those scenarios is handled through event handlers and a working example of each is shown below.
// Get the DOM references you'll need just once:
var feedback = document.getElementById("feedback");
// Don't set variables equal to property values of DOM elements because
// if you decide you need a different property value, you have to re-scan
// the DOM for the same element all over again.
var p1 = document.getElementById("p1")
var p2 = document.getElementById("p2");
var form = document.querySelector("form");
// Use this to validate when submit is pressed (causing form to be submitted):
form.addEventListener("submit", function(evt){
// If validate function returns false, don't submit
if(!validate()){
evt.preventDefault(); // cancel the form submission
feedback.textContent = "Can't submit. Form is not valid!";
}
});
// Get the elements that need to be validated:
var inputs = document.querySelectorAll("input[type=text],input[type=password]");
// Convert that node list into an array:
inputs = Array.prototype.slice.call(inputs);
// Loop over array and set up event handlers for inputs
inputs.forEach(function(input){
input.addEventListener("blur", validate); // Used to validate when user moves off of each element
input.addEventListener("input", validate); // Used to validate as data is being entered
});
function validate() {
// Keep track of whether the form is valid or not. Assume that it is by default
var valid = true;
// .innerHTML is for when you want to assign a string containing
// HTML to a DOM element. This invokes the HTML parser and renders
// the HTML. If you don't have HTML in the string, use .textContent
// instead, which doesn't invoke the HTML parser and is more efficient
// See if the password was typed in both boxes before telling the user
// that the passwords don't match
if(p1.value && p2.value){
// Are they the same?
if(p1.value !== p2.value){
feedback.textContent = "passwords dont match";
valid = false;
} else {
feedback.textContent = "passwords match";
}
} else {
// If both password fields aren't filled in, the form can't be valid
valid = false;
}
if(p1.value === "") {
feedback.textContent = "Must have a password";
valid = false;
}
// Send a result to the caller so it can be known by other code if the form is valid
return valid;
}
<div class = "mainInfo">
<form name="SignUpForm" action="signUpHandler.php" method='POST' class="inputLists">
<div>username: <input type="text" name="userName"></div>
<div>password: <input id="p1" type="password" name="password"></div>
<div>reenter password: <input id="p2" type="password" name="passwordConfirmation"></div>
<!-- Any form element that has a "name" attribute will submit its name/value as
part of the form data when the form gets submitted. You probably don't want
the actual submit button to be included in this, so don't give the button
a "name" attribute. -->
<input type="submit" value="submit"> <input type="reset" value="reset">
</form>
<div id="feedback"></div>
</div>

How to correctly validate form with jQuery?

So I have this jQuery for my form:
frm.submit(function(event) {
validateForm();
if(validateForm()) {
$(this).submit();
} else {
event.preventDefault();
}
});
It does sort of work, but I get JS <error> (and it doesn't say anything else in the console about it), I think the reason is that the function has to go through the validation again? Kind of like a circular dependency.
Can you show me a better way to do the exact thing that I'm trying to achieve here please?
Validate and show errors if filled in the wrong way;
Submit the form if everything is ok
Something like this maybe?
HTML -
<input type="text" id="name" />
<input type="tel" id="phone" />
<button type="button" id="submit">Submit</button>
<div id="errors"></div>
JS -
const user = {}
$('#submit').click(function(){
// validating form
if(!$('#name').val()) {
$('#errors').text('invalid value in "name" field')
return;
}
if(!$('#phone').val()) {
$('#errors').text('invalid value in "phone" field')
return;
}
$('#errors').text('');
user.phone = $('#phone').val();
user.name = $('#name').val();
// form submission goes here
});
Logic -
Once a function returns, the execution of anything else after the return expression itself, is prevented.
If you don't return anything, the interpreter will continue to the next expression.
This gives you the option of manipulating elements and handle errors just before returning and stopping the function from continuing to run.
function validateForm(){
if (input.val().isok && select.val().ispresent){
form.submit();
}else{
show_errors();
}
}
why not that way?

javascript - why doesnt this work?

<form method="post" action="sendmail.php" name="Email_form">
Message ID <input type="text" name="message_id" /><br/><br/>
Aggressive conduct <input type="radio" name="conduct" value="aggressive contact" /><br/><br/>
Offensive conduct <input type="radio" name="conduct" value="offensive conduct" /><br/><br/>
Rasical conduct <input type="radio" name="conduct" value="Rasical conduct" /><br/><br/>
Intimidating conduct <input type="radio" name="conduct" value="intimidating conduct" /><br/><br/>
<input type="submit" name="submit" value="Send Mail" onclick=validate() />
</form>
window.onload = init;
function init()
{
document.forms["Email_form"].onsubmit = function()
{
validate();
return false;
};
}
function validate()
{
var form = document.forms["Email_form"]; //Try avoiding space in form name.
if(form.elements["message_id"].value == "") { //No value in the "message_id"
box
{
alert("Enter Message Id");
//Alert is not a very good idea.
//You may want to add a span per element for the error message
//An div/span at the form level to populate the error message is also ok
//Populate this div or span with the error message
//document.getElementById("errorDivId").innerHTML = "No message id";
return false; //There is an error. Don't proceed with form submission.
}
}
}
</script>
Am i missing something or am i just being stupid?
edit***
sorry i should add! the problem is that i want the javascript to stop users going to 'sendmail.php' if they have not entered a message id and clicked a radio button... at the moment this does not do this and sends blank emails if nothing is inputted
You are using
validate();
return false;
...which means that the submit event handler always returns false, and always fails to submit. You need to use this instead:
return validate();
Also, where you use document.forms["Email form"] the space should be an underscore.
Here's a completely rewritten example that uses modern, standards-compliant, organised code, and works:
http://jsbin.com/eqozah/3
Note that a successful submission of the form will take you to 'sendmail.php', which doesn't actually exist on the jsbin.com server, and you'll get an error, but you know what I mean.
Here is an updated version that dumbs down the methods used so that it works with Internet Explorer, as well as includes radio button validation:
http://jsbin.com/eqozah/5
You forgot the underscore when identifying the form:
document.forms["Email_form"].onsubmit = ...
EDIT:
document.forms["Email_form"].onsubmit = function() {
return validate();
};
function validate() {
var form = document.forms["Email_form"];
if (form.elements["message_id"].value == "") {
alert("Enter Message Id");
return false;
}
var conduct = form.elements['conduct']; //Grab radio buttons
var conductValue; //Store the selected value
for (var i = 0; i<conduct.length; i++) { //Loop through the list and find selected value
if(conduct[i].checked) { conductValue = conduct[i].value } //Store it
}
if (conductValue == undefined) { //Check to make sure we have a value, otherwise fail and alert the user
alert("Enter Conduct");
return false;
}
return true;
}
return the value of validate. Validate should return true if your validation succeeds, and false otherwise. If the onsubmit function returns false, the page won't change.
EDIT: Added code to check the radio button. You should consider using a javascript framework to make your life easier. Also, you should remove the onclick attribute from your submit input button as validation should be handled in the submit even, not the button's click
Most obvious error, your form has name attribute 'Email_form', but in your Javascript you reference document.forms["Email form"]. The ironic thing is, you even have a comment in there not to use spaces in your form names :)

Categories

Resources