I'm trying to write easy validation code and I have trouble. I've created element div '._error-alert' and I cant remove it if the input isn't empty.
When I press submit appears my element '._error-alert' but it doesnt disapear when I try to type something there. I'll be very grateful if u help or at least show me the other path to solve it
const form = document.querySelector('.validation__form'),
reqItems = document.querySelectorAll('._req'),
emailTest = /^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()\.,;\s#\"]+\.{0,1})+[^<>()\.,;:\s#\"]{2,})$/,
onlyTextTest = /^[a-zA-Z0-9#]+$/,
onlyNums = /^[0-9]+$/;
const inputTest = (example, input) => example.test(input.value);
const formAddError = (input) => {
if (input.classList.contains('_req')) {
const createBlock = document.createElement('div');
createBlock.classList.add('_error-alert');
input.parentElement.insertAdjacentElement("beforeend", createBlock);
createBlock.innerText = `Invalid ${input.getAttribute("name")}!`;
}
input.parentElement.classList.add('_error');
input.classList.add('_error');
};
const formRemoveError = (input) => {
input.parentElement.classList.remove('_error');
input.classList.remove('_error');
};
// validates form if function validateForm didn't have any errors and removes my created elements '._error-alert'
const sendValidatedForm = (e) => {
e.preventDefault();
let error = validateForm(form);
if (error === 0) {
console.log('fine');
form.reset();
document.querySelectorAll('._error-alert').forEach((errorAlert) => {
errorAlert.remove();
});
}
};
form.addEventListener('submit', sendValidatedForm);
// there I want to check input and remove '._error-alert' if input isnt wrong
const checkInput = () => {
reqItems.forEach((reqInput, index) => {
reqInput.addEventListener('input', () => {
formRemoveError(reqInput);
});
});
};
checkInput();
const validateForm = (form) => {
let error = 0;
reqItems.forEach(reqInput => {
reqInput.value.trim();
formRemoveError(reqInput);
if (reqInput.getAttribute("name") == "email") {
if (!inputTest(emailTest, reqInput)) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "phone") {
if (!inputTest(onlyNums, reqInput) && reqInput.value.length < 8) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "name") {
if (!inputTest(onlyTextTest, reqInput)) {
formAddError(reqInput);
error++;
}
}
});
console.log(error);
return error;
};
<form action="" class="validation__form">
<div class="validation__input-list">
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="name" placeholder="Name">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="surname" placeholder="Surname">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="phone" placeholder="Phone">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="email" placeholder="Email">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="password" placeholder="Password">
</div>
</div>
<button class="validation__form-btn">Submit</button>
</form>
Set the css visibility property of the element to hidden.
const error_element = document.getElementsByClassName('_error-alert')
error_element.style.visibility = 'hidden'
Related
here's simple html form validated with JavaScript , it show error message when the required fields are empty and when username is short(not satisfy the required length)..but form is sending data when submit button is clicked even if errors are displayed....how can i properly validate this form?
here's the code
let form = document.getElementById("signUp");
let uname = document.querySelector("#userName");
let uemail = document.querySelector("#userEmail");
const showError = (input, message) => {
// get the form-field element
const formField = input.parentElement;
// show the error message
const error = formField.querySelector('small');
error.textContent = message;
};
const showSuccess = (input) => {
// get the form-field element
const formField = input.parentElement;
// hide the error message
const error = formField.querySelector('small');
error.textContent = '';
}
const validateForm = () => {
if (uname.value.trim() == "") {
showError(uname, "name is empty");
return false;
} else {
showSuccess(uname);
}
if (uemail.value.trim() == "") {
showError(uemail, "email is empty");
return false;
} else {
showSuccess(uemail);
}
return true;
}
const checkUsername = () => {
const username = uname.value.trim();
if (username.length < 3) {
showError(uname, 'Username must be atleast 4 characters')
return false;
} else {
showSuccess(uname);
}
return true;
}
form.addEventListener('input', (event) => {
switch (event.target.id) {
case 'userName':
checkUsername();
break;
}
});
form.addEventListener("submit", function(e) {
if (!validateForm()) {
e.preventDefault();
}
});
<h2>JavaScript Validation</h2>
<form id="signUp" name="myForm">
<div>
Name: <input type="text" name="uname" id="userName">
<small id="showMessage" class="form-text text-muted"></small>
</div>
<div>
<br> Email: <input type="email" name="email" id="userEmail">
<small id="showMessage" class="form-text text-muted"></small>
</div>
<div>
<button type="submit">sign up</button>
</form>
What I'm trying to do is make it so if a text field does not contain the "required" class and the field is empty, no error message will show up when the form is validated. The problem I have right now is that the password error message still shows up for the field without the "required" class. I've tried adding an if statement to the validate field function to check if the field doesn't contain the "required" class, but that doesn't appear to be working. Help much appreciated.
Functions:
const validateForm = (e) => {
e = e.target;
let messages = [];
validateRequired(e, messages);
validatePassword(e, messages);
if (messages.length > 0) {
let ul = document.createElement("ul");
for (const message of messages) {
let li = document.createElement("li")
li.textContent = message;
ul.appendChild(li);
}
e.parentElement.getElementsByClassName("errors")[0].innerHTML = "";
e.parentElement.getElementsByClassName("errors")[0].appendChild(ul);
return false;
}
return true;
}
const validateRequired = (e, messages) => {
let fields = e.getElementsByClassName("required");
for (const field of fields) {
let input = field.value;
if (input.trim().length === 0) {
messages.push("Error.");
break;
}
}
}
const validatePassword = (e, messages) => {
let fields = e.getElementsByClassName("password");
for (const field of fields) {
let input = field.value;
let allCharacters = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]*$/;
if (!input.match(allCharacters)) {
messages.push("Error.");
break;
}
}
}
Form:
<div>
<label for="password">Password : </label>
<input type="text" name="password" id="password" class="password" />
</div>
<div>
<label for="requiredPassword">Required and Password : </label>
<input type="text" name="requiredPassword" id="requiredPassword" class="required password" />
I hope somebody can help me find the error in this code, i need to use simple js to create a form that only submits if there are no errors, The user name must be a valid email. The password and retyped password must be 8 characters and include one uppercase, one lowercase and one numeric. The password
and the retyped password must match. I need to make use of a regular
expression to constrain the password.
If the data rules are violated, a appropriate error messages should be displayed
and the form should be stopped from submitting.Can someone help me with why it is not doing what it is supposed to? Im still new to js and any hel would be appreciated.
function handleInvalidities(input) {
let errMsg = " ";
if (!input.validity.paternMismatch) {
errMsg = "Invalid entry. Enter your details in the format shown.";
}
if (!input.validity.paternMismatch) {
errMsg = "Invalid entry. This field cannot be empty. Please enter a value.";
}
return errMsg;
}
function displayInvalidities(errMsg, elem) {
let elemPos = document.getElementById(elem);
let errElem = document.createElement("span");
errElem.setAttribute("class", "error");
let errText = document.createTextNode(errMsg);
errElem.appendChild(errText);
elemPos.parentNode.insertBefore(errElem, elemPos.nextSibling);
}
function cleanUpErrors() {
let errors = document.getElementsByClassName("error");
for (let i = 0; i < errors.length; i++) {
errors[i].style.display = "none";
}
}
window.onload = () => {
let theForm = document.getElementById("loginform");
theForm.addEventListener("submit");
(event) => {
let stopSubmit = false;
cleanedUpErrors();
for (let i = 0; i < theForm.elements.length; i++) {
if (!theForm.elements[i].checkValidity()) {
displayInvalidities(handleInvalidities(theForm.elements[i]), theForm.Elements[i].id);
stopSubmit = true;
}
}
if (stopSubmit) {
event.preventDefault();
}
}, (false);
}
<section>
<h1>Form: validated using Javascript</h1>
<p>Try entering the following:</p>
<ul>
<li>Password longer or shorter than 8 characters and/or without an uppercase, lowercase or a numeric.</li>
<li>Passwords that do not match</li>
</ul>
<h2>Register</h2>
<p>* = Required Field</p>
<div id="formcontainer">
<form id="regsiterdetails" action="fma_t3confirm.html">
<div>
<label for="username">* Userame:</label>
<input type="text" id="username">
</div>
<div>
<label for="password">* Password (Must be 8 characters and include one uppercase, one lowercase and one numeric):</label>
<input type="password" id="password">
<input type="checkbox" id="showpasswords">
<label id="showpasswordslabel" for="showpasswords">Show passwords</label>
</div>
<div>
<label for="retypedpassword">* Retype your password:</label>
<input type="password" id="retypedpassword">
<span id="passwordmatcherror"></span>
</div>
<div>
<button type="submit" id="registerButton">Register</button>
</div>
</form>
</div>
</section>
You need to fix your event handlers and spelling (Elements) and form ID (registerdetails) and spelling of function names like cleanUpErrors
window.addEventListener("load", () => {
document.getElementById("registerdetails").addEventListener("submit", event => {
const theForm = event.target;
let stopSubmit = false;
cleanUpErrors();
for (let i = 0; i < theForm.elements.length; i++) {
if (!theForm.elements[i].checkValidity()) {
displayInvalidities(handleInvalidities(theForm.elements[i]), theForm.elements[i].id);
stopSubmit = true;
}
}
if (stopSubmit) {
event.preventDefault();
}
})
})
You are setting event listener without any function. Change in your code like this:
window.onload = () => {
let theForm = document.getElementById("regsiterdetails");
theForm.addEventListener("submit",(event) => {
let stopSubmit = false;
cleanedUpErrors();
for (let i = 0; i < theForm.elements.length; i++) {
if (!theForm.elements[i].checkValidity()){
displayInvalidities(handleInvalidities(theForm.elements[i]), theForm.Elements[i].id);
stopSubmit = true;
}
}
if (stopSubmit) {
event.preventDefault();
}
}, (false);
)
}
Also check your code as there is no element with id loginform. I think you should use regsiterdetails instead of loginform.
There was no form element with the id that you're trying to get. Try to get with the actual id which is regsiterdetails and fix your addEventListener parameter list.
function handleInvalidities(input) {
let errMsg = " ";
if (!input.validity.paternMismatch) {
errMsg = "Invalid entry. Enter your details in the format shown.";
}
if (!input.validity.paternMismatch) {
errMsg = "Invalid entry. This field cannot be empty. Please enter a value.";
}
return errMsg;
}
function displayInvalidities(errMsg, elem) {
let elemPos = document.getElementById(elem);
let errElem = document.createElement("span");
errElem.setAttribute("class", "error");
let errText = document.createTextNode(errMsg);
errElem.appendChild(errText);
elemPos.parentNode.insertBefore(errElem, elemPos.nextSibling);
}
function cleanUpErrors() {
let errors = document.getElementsByClassName("error");
for (let i = 0; i < errors.length; i++) {
errors[i].style.display = "none";
}
}
window.onload = () => {
let theForm = document.getElementById("regsiterdetails");
theForm.addEventListener("submit", (event) => {
let stopSubmit = false;
cleanedUpErrors();
for (let i = 0; i < theForm.elements.length; i++) {
if (!theForm.elements[i].checkValidity()) {
displayInvalidities(handleInvalidities(theForm.elements[i]), theForm.Elements[i].id);
stopSubmit = true;
}
}
if (stopSubmit) {
event.preventDefault();
}
}, (false));
}
<section>
<h1>Form: validated using Javascript</h1>
<p>Try entering the following:</p>
<ul>
<li>Password longer or shorter than 8 characters and/or without an uppercase, lowercase or a numeric.</li>
<li>Passwords that do not match</li>
</ul>
<h2>Register</h2>
<p>* = Required Field</p>
<div id="formcontainer">
<form id="regsiterdetails" action="fma_t3confirm.html">
<div>
<label for="username">* Userame:</label>
<input type="text" id="username">
</div>
<div>
<label for="password">* Password (Must be 8 characters and include one uppercase, one lowercase and one numeric):</label>
<input type="password" id="password">
<input type="checkbox" id="showpasswords">
<label id="showpasswordslabel" for="showpasswords">Show passwords</label>
</div>
<div>
<label for="retypedpassword">* Retype your password:</label>
<input type="password" id="retypedpassword">
<span id="passwordmatcherror"></span>
</div>
<div>
<button type="submit" id="registerButton">Register</button>
</div>
</form>
</div>
</section>
I have seen this method of serializing a form to JSON and it's working fine. My question is: How can I achieve this with pure JavaScript, without using any jQuery code? I am sorry if the question is dumb, but I'm still learning so if anyone can help me, I'll be grateful.
(function ($) {
$.fn.serializeFormJSON = function () {
var objects = {};
var anArray = this.serializeArray();
$.each(anArray, function () {
if (objects[this.name]) {
if (!objects[this.name].push) {
objects[this.name] = [objects[this.name]];
}
objects[this.name].push(this.value || '');
} else {
objects[this.name] = this.value || '';
}
});
return objects;
};
})(jQuery);
$('form').submit(function (e) {
e.preventDefault();
var data = $(this).serializeFormJSON();
console.log(data);
/* Object
email: "value"
name: "value"
password: "value"
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="#" method="post">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" />
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<p>
<input type="submit" value="Send" />
</p>
</form>
P.S.
Also in jQuery is this the right way to send multiple JSON objects from user input as One String, because I am searching for a way to do that?
You can try something like this:
function formToJson(){
var formElement = document.getElementsByTagName("form")[0],
inputElements = formElement.getElementsByTagName("input"),
jsonObject = {};
for(var i = 0; i < inputElements.length; i++){
var inputElement = inputElements[i];
jsonObject[inputElement.name] = inputElement.value;
}
return JSON.stringify(jsonObject);
}
This solution works only if you have a single form on the page, to make it more general the function could e.g. take the form element as an argument.
You can use Array.reduce, something like
// get array of all fields and/or selects (except the button)
const getFields = () => Array.from(document.querySelectorAll("input, select"))
.filter(field => field.type.toLowerCase() !== "button");
// get id, name or create random id from field properties
const getKey = field => field.name
|| field.id
|| `unknown-${Math.floor(1000 * Math.random()).toString(16)}`;
// get data, simple object
const getFormData = () => getFields()
.reduce( (f2o, field) => ({...f2o, [getKey(field)]: field.value}), {} );
// log the result
const logResult = txt => document.querySelector("#result").textContent = txt;
// get data, array of field objects
const getMoreFormData = () => getFields()
.reduce( (f2o, field) =>
f2o.concat({
id: field.id || "no id",
name: field.name || "no name",
idGenerated: getKey(field),
type: field.type,
value: field.value }
),
[] );
// handling for buttons
document.addEventListener("click", evt => {
if (evt.target.nodeName.toLowerCase() === "button") {
console.clear();
logResult(/simple/.test(evt.target.textContent)
? JSON.stringify(getFormData(), null, " ")
: JSON.stringify(getMoreFormData(), null, " ")
);
}
} );
<form action="#" method="post">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" value="Pete"/>
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" value="pete#here.com"/>
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<div>
<label>Field without name or id</label>
<input type="number" value="12345" />
</div>
</form>
<p>
<button>Data simple object</button> <button>Data fields array</button>
</p>
<pre id="result"></pre>
Remember that for checkboxes, value attribute can be either on or off string. This is unwanted. Here is my solution, based on this codepen.
let json = Array.from(form.querySelectorAll('input, select, textarea'))
.filter(element => element.name)
.reduce((json, element) => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
return json;
}, {});
OR
let json = {};
Array.from(form.querySelectorAll('input, select, textarea'))
.filter(element => element.name)
.forEach(element => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
});
OR (with typescript)
export type FormJson = {[key: string]: boolean | string};
export const toJson = (form: HTMLFormElement): FormJson =>
Array.from(form.querySelectorAll<HTMLFormElement>('input, select, textarea'))
.filter(element => element.name)
.reduce<FormJson>((json, element) => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
return json;
}, {});
To serialize your form you can do this (note that I added an onsubmit in the form tag):
HTML and JavaScript:
function serializeForm(e) {
e.preventDefault(); // prevent the page to reload
let form = e.target; // get the form itself
let data = new FormData(form); // serialize input names and values
let objSerializedForm = {}; // creating a new object
for (let [name, value] of data) { // iterating the FormData data
objSerializedForm[name] = value; // appending names and values to obj
}
console.log(objSerializedForm);
}
<form action="#" method="post" onsubmit="serializeForm(event)">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" />
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<p>
<input type="submit" value="Send" />
</p>
</form>
Than you can do whatever you want with your objSerializedForm, getting each value by calling objSerializedForm.name.
I would like to find a way to focus on the next field when I click enter in the input using React.js
#autobind
handleKeyPress(event){
if(event.key === 'Enter'){
this.refs.email.focus();
}
}
#autobind
handleKeyPressEmail(event){
if(event.key === 'Enter'){
this.refs.zip_code.focus();
}
}
<input
onKeyPress={this.handleKeyPress}
ref = 'name'
/>
<input
onKeyPress={this.handleKeyPressEmail}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
This is the best way I have found so far, however I don't want to repeat myself by creating a function everytime I want that to happen. Is there a better and cleaner way to implement this?
If <form> is present:
function handleEnter(event) {
if (event.keyCode === 13) {
const form = event.target.form;
const index = Array.prototype.indexOf.call(form, event.target);
form.elements[index + 1].focus();
event.preventDefault();
}
}
...
<form>
<input onKeyDown={handleEnter} />
<input onKeyDown={handleEnter} />
<input />
</form>
CodePen
Without <form>:
function useFocusNext() {
const controls = useRef([]);
const handler = (event) => {
if (event.keyCode === 13) {
// Required if the controls can be reordered
controls.current = controls.current
.filter((control) => document.body.contains(control))
.sort((a, b) =>
a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
? -1 : 1
);
const index = controls.current.indexOf(event.target);
const next = controls.current[index + 1];
next && next.focus();
// IE 9, 10
event.preventDefault();
}
};
return useCallback((element) => {
if (element && !controls.current.includes(element)) {
controls.current.push(element);
element.addEventListener('keydown', handler);
}
}, []);
};
...
const focusNextRef = useFocusNext();
<input ref={focusNextRef} />
<input ref={focusNextRef} />
<button ref={focusNextRef}>Submit</button>
CodePen
You can use componentDidMount and auto bind refs through a for-in loop.
http://codepen.io/jzmmm/pen/PzZgRX?editors=0010
constructor() {
super();
this._handleKeyPress = this._handleKeyPress.bind(this);
}
// Loop through the ref's object, and bind each of them to onkeypress
componentDidMount() {
for (let x in this.refs) {
this.refs[x].onkeypress = (e) =>
this._handleKeyPress(e, this.refs[x]);
}
}
// This checks ENTER key (13), then checks if next node is an INPUT
// Then focuses next input box
_handleKeyPress(e, field) {
if (e.keyCode === 13) {
e.preventDefault(); // Prevent form submission if button present
let next = this.refs[field.name].nextSibling;
if (next && next.tagName === "INPUT") {
this.refs[field.name].nextSibling.focus();
}
}
}
render() {
return (
<form>
<input type="text" name="name" ref='name' />
<input type="text" name="email" ref='email' />
<input type="text" name="zip_code" ref='zip_code' />
</form>
);
}
Without <form> and TypeScript version.
Skip disabled inputs.
const onKeyPress: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (e.key === "Enter") {
const inputs = Array.from(
// Get table or tbody whatever that contains all inputs. The number of parentElements depends on the structure of your html
e.currentTarget?.parentElement?.parentElement?.parentElement?.querySelectorAll(
"input"
) ?? []
).filter((e) => !e.disabled)
const index = inputs.indexOf(e.currentTarget)
inputs[index + 1]?.focus()
e.preventDefault()
}
},
[]
)
return <input type="number" onKeyPress={onKeyPress} />
This is how I managed to make it simpler:
#autobind
handleKeyPress(value, event){
if(event.key === 'Enter'){
this.refs[event].focus();
}
}
<input
onKeyPress={(event) => this.handleKeyPress('email', event)}
ref = 'name'
/>
<input
onKeyPress={(event) => this.handleKeyPress('zip_code', event)}
ref = 'email'
/>
<input
ref = 'zip_code'
/>