I've this structure here:
<div>
<div class="row">
<input id="1a">
<input id="1b">
</div>
<div class="row">
<input id="2a">
<input id="2b">
</div>
<div class="row">
<input id="3a">
<input id="3b">
</div>
<div class="row">
<input id="4a">
<input id="4b">
</div>
</div>
If the user leaves everything empty, there is no problem. But when he enters for example something into 1a and leaves 1b empty, this should cause an error. So how can I find out if a & b is filled for each row? It's a bit tricky and I have no idea how to deal with this.
You can achieve it in this simple way
$('button').on("click", () => {
$('body').find(".row").each(function(index, row){
var count = 0;
$(row).find("input").each(function(i, input) {
if($(input).val() !== "")
count++;
})
if(count === 1)
alert("Row " + (index + 1) + " is invalid");
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class="row">
<input id="1a">
<input id="1b">
</div>
<div class="row">
<input id="2a">
<input id="2b">
</div>
<div class="row">
<input id="3a">
<input id="3b">
</div>
<div class="row">
<input id="4a">
<input id="4b">
</div>
</div>
<button>Check</button>
something like that ?
const inRow = document.querySelectorAll('.row')
, valida = document.querySelector('button')
, respon = document.querySelector('output')
;
valida.onclick =_=>
{
let OK = true
inRow.forEach(eR=>
{
let vals = 0
eR.querySelectorAll('input').forEach(eI=>{ vals+=eI.value.length ? 1:0 })
if (vals===1) OK=false
})
respon.value = OK ? 'OK' : 'bad'
}
<div>
<div class="row"> <input id="1a"> <input id="1b"> </div>
<div class="row"> <input id="2a"> <input id="2b"> </div>
<div class="row"> <input id="3a"> <input id="3b"> </div>
<div class="row"> <input id="4a"> <input id="4b"> </div>
</div>
<button>validate</button> <output></output>
Normally multiple form controls (ex <input>, <textarea>, <select>, etc) should be inside a <form> tag. Moreover, the behavior described is called form validation which requires said <form> tag to be triggered by a "submit" event.
The following demo features proper HTML
<form> and <fieldset> instead of <div>
added <output> for each <fieldset>
Also the JavaScript is designed to show a message when any pair of <input> has a .value and the other doesn't.
The message: <output>Complete data input</output>
This pseudo-form validation is triggered whenever a user enters data within an <input> and then clicks (aka "blur" event). This entire chain of actions combined is the "change" event.
The <form> tag is registered to the "change" event so if any of the <input> within the <form> is the event origin (aka event.target - the <input> that the user triggers a "change" event on).
const form = document.forms[0];
form.onchange = reqData;
function reqData(event) {
let origin = event.target;
if (origin.tagName === 'INPUT') {
const parent = origin.parentElement;
const output = parent.querySelector('output');
const inputs = [...parent.querySelectorAll('input')];
let total = inputs.length;
let count = 0;
for (let input of inputs) {
if (input.value) {
count++;
}
}
if (count < total && count > 0) {
output.style.opacity = '1';
} else {
output.style.opacity = '0';
}
}
return false;
}
output {
opacity: 0;
color: tomato
}
<form>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
</form>
Related
I am currently trying to create a tip calculator app using JS, HTML, and CSS. My issue is that the input value is submitted when the button is clicked, but once submitted, the value just flashes for less than a second, then it vanishes. I would like for the value to stay on the screen once submitted.
let dinTotal = document.querySelector('#cost');
let dinService = document.querySelector('#service');
let dinSize = document.querySelector('#size');
let calcBtn = document.querySelector('button');
let total = 0;
let amount = document.querySelector('#amount')
calcBtn.addEventListener('click', function() {
if(dinTotal.value >= 50 && dinService.value < 5 && dinSize.value < 5) {
total = (dinTotal.value * 0.10) + dinTotal.value ;
amount.textContent = total;
}
})
<form>
<h1>Tip & Dip✌️</h1>
<hr>
<!-- Bill Section -->
<div>
<label for='cost'>Dinning amount</label>
</div>
<input name='cost' id='cost' type='number' placeholder='$' required>
<!-- Service Section-->
<div class='top-space'>
<label for='service'>How was the service</label>
</div>
<input name='service' id='service' type='number' placeholder='rate 1-10' required>
<!-- Party Size-->
<div class='top-space'>
<label for='size'>Party Size</label>
</div>
<div>
<input name='size' id='size' type='number' required>
</div>
<div class='top-space'>
<button>LET'S CALCULATE...</button>
</div>
<hr>
<h2>Total: $<span id='amount'>0</span></h2>
</form>
The default behavior of a button is to submit the form. When a form submits, if you don't stop it, it will unload the current page and submit the data to its action URL. With no action attribute, a form submits to the current page, causing your reload.
Set the type attribute on your button to button to prevent it from being a submit button.
<button type="button">LET'S CALCULATE...</button>
use the preventDefualt() method of the event callback
calcBtn.addEventListener('click', function(event) {
event.preventDefault()
}
Full working snippet (though it only updates for size and value of less than 5 and checks greater than 50 I hope you know)
let dinTotal = document.querySelector('#cost');
let dinService = document.querySelector('#service');
let dinSize = document.querySelector('#size');
let calcBtn = document.querySelector('button');
let total = 0;
let amount = document.querySelector('#amount')
calcBtn.addEventListener('click', function(event) {
event.preventDefault()
if(dinTotal.value >= 50 && dinService.value < 5 && dinSize.value < 5)
{
total = (dinTotal.value * 0.10) + dinTotal.value ;
console.log(total)
amount.textContent = total;
}
})
<form>
<h1>Tip & Dip✌️</h1>
<hr>
<div>
<label for='cost'>Dinning amount</label>
</div>
<input name='cost' id='cost' type='number' placeholder='$' required>
<div class='top-space'>
<label for='service'>How was the service</label>
</div>
<input name='service' id='service' type='number' placeholder='rate 1-10' required>
<div class='top-space'>
<label for='size'>Party Size</label>
</div>
<div>
<input name='size' id='size' type='number' required>
</div>
<div class='top-space'>
<button>LET'S CALCULATE...</button>
</div>
<hr>
<h2>Total: $<span id='amount'>0</span></h2>
</form>
In last question I had assistance to create a code to save the state of a fairly complex toggle state. When a radio button is selected a checkbox slides down, when that checkbox is selected another one slides down. The reverse also occurs. Much of the code I do not understand. The problem now is that it works perfectly in jsfiddle.
https://jsfiddle.net/tomik23/ovysmutL/7/
However, it does not function on my webpage. When localstorage restores the state back to the webpage after a page refresh it automatically 'unchecks' the checkboxes about 2 seconds after load when they should have remained checked.
I totally stripped down my page to try to isolate this issue...the problem still exists. I read and tried all the similar stackoverflow problems eg( modified forms, added doc ready function,etc)... but obviously none of them helped.
The code works prior to localstorage insertion. When page is refreshed localstorage restores the state back to the webpage but it automatically 'unchecks' the checkboxes about 2 seconds after load when they should have remained checked. Does ANYBODY know what is going on AND HOW TO FIX THIS? I truly appreciate the help.
**HTML**
<form class="form" id="form-a" method="post" autocomplete="on">
<fieldset>
<div>
<p>
<label class="yes_text">Yes</label>
<span>
<input type="radio" data-show="next-a" id="dog" name="answer_name" value="yes">
</span>
</p>
<p>
<label>No</label>
<span>
<input type="radio" name="answer_name" value="no" checked>
</span>
</p>
</div>
</fieldset>
<fieldset id="next-a" class="hidden">
<div class="red">
<div>
<p>Include Red Dogs:</p>
</div>
<div>
<p>
<label class="yes_text_include">select to include</label>
<span>
<input type="checkbox" data-show="next-b" id="cat" class="red" name="red_name" value="">
</span>
</p>
</div>
</div>
<div id="next-b" class="hidden">
<div>
<p>Include Green Dogs:</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="green_name" class="cat" value="">
</span>
</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="blue_name" class="cat" value="">
</span>
</p>
</div>
</div>
</fieldset>
</form>
<form class="form" id="form-b" method="post" autocomplete="on">
<fieldset>
<div>
<p>
<label class="yes_text">Yes</label>
<span>
<input type="radio" data-show="next-a" id="dog" name="answer_name" value="yes">
</span>
</p>
<p>
<label>No</label>
<span>
<input type="radio" name="answer_name" value="no" checked>
</span>
</p>
</div>
</fieldset>
<fieldset id="next-a" class="hidden">
<div class="red">
<div>
<p>Include Red Dogs:</p>
</div>
<div>
<p>
<label class="yes_text_include">select to include</label>
<span>
<input type="checkbox" data-show="next-b" id="cat" class="red" name="red_name" value="">
</span>
</p>
</div>
</div>
<div id="next-b" class="hidden">
<div>
<p>Include Green Dogs:</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="green_name" class="cat" value="">
</span>
</p>
</div>
</div>
</fieldset>
</form>
**Javascript**
class CheckForm {
constructor(option) {
const forms = document.querySelectorAll(`${option}`);
forms.forEach(form => {
const formname = form.id;
this.formCheck(formname);
this.checkChecked(formname);
});
}
formCheck(formName) {
const form = document.querySelector(`#${formName}`);
form.addEventListener('click', e => {
const { target: { type, value, id, dataset: { show } } } = e;
switch (type) {
case 'radio': {
if (value === 'yes') {
$(`#${formName}`).find(`#${show}`).show(200);
this.saveToLocalstore(formName);
} else {
$(`#${formName} fieldset`).next().hide(200);
document.querySelector(`#${formName}`).reset();
localStorage.removeItem(formName);
this.removeAllChecked(formName);
}
break;
}
case 'checkbox': {
$(`#${formName}`).find(`#${show}`).toggle(200);
this.saveToLocalstore(formName);
if (id) {
this.removeAllChecked(formName, id);
}
break;
}
default:
break;
}
});
}
saveToLocalstore(formName) {
let allInput = document.querySelectorAll(`#${formName} input`);
let object = {};
allInput.forEach(item => {
object[item.name] = item.type === 'radio' ? true : item.checked;
});
localStorage.setItem(formName, JSON.stringify(object));
}
checkChecked(formName) {
const data = JSON.parse(localStorage.getItem(formName));
if (data) {
let i = 1;
for (let [key, value] of Object.entries(data)) {
const item = document.querySelector(`#${formName} input[name='${key}']`);
setTimeout(() => {
if (value) {
item.click();
}
}, i * 1000);
i++;
}
}
}
removeAllChecked(formName, id) {
if (id) {
let allInput = document.querySelectorAll(`#${formName} .${id}`);
allInput.forEach(item => {
item.checked = false;
});
} else {
const allHidden = document.querySelectorAll(`#${formName} .hidden`);
allHidden.forEach(item => {
item.removeAttribute('style', '');
});
}
}
}
new CheckForm('.form');
**CSS**
.hidden {
display: none;
}
So I have a form with two identical group of inputs that represent education info. There could be more than two as I want to include a button to create a new group so the user can put all his education background like in LinkedIn.
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
</form>
Now, if I use the FormData API to get the form data like this:
for(let entry of formData.entries()){
console.log(entry);
}
I get the following output:
(2) ["institutionName", "Harvard"]
(2) ["courseName", "Web Development"]
(2) ["startDate", "2000-11"]
(2) ["endDate", "2008-11"]
(2) ["institutionName", "Oxford"]
(2) ["courseName", "Business Management"]
(2) ["startDate", "2009-10"]
(2) ["endDate", "2010-05"]
What I want to achieve is to get the output in an organized way, like this:
education:[
{
institutionName:"Harvard",
courseName:"Web Development",
startDate:"2000-11",
endDate:"2008-11"
},
{
...
}
]
So I'm interested in knowing the best approach to achieve this. Thanks in advance for any help!
It does not make sense to have two equal forms, with one being sufficient.
In addition to the form you should have a list that shows each item added.
It's what I recommend.
Not sure whether this is the best approach, but you can achieve the desired structure like this:
const formCV = document.querySelector('#formCV');
const formData = new FormData(formCV);
function groupEducationData(inputGroupSize = 4) {
const result = [];
let educationObj = null;
let counter = 0;
for (const entry of formData.entries()) {
// Since the counter is divisible by the number of inputs in a group
// only if one form group finishes. And when one form group finishes,
// we need to add the object into the result array
if (counter % inputGroupSize === 0) {
// if this is the first iteration, the educationObj is null and
// we don't want to add it to the result array yet
// we only add the educationObj to the result array if it is
// an object containing the education info
if (educationObj) result.push(educationObj);
// initialize the educationObj at the start
// and after one form finishes
educationObj = {};
}
// add entry[0] as key to the object (e.g. 'institutionName')
// with the value of entry[1] (e.g. 'Harvard')
educationObj[entry[0]] = entry[1];
counter++;
}
return result.concat(educationObj);
}
console.log(groupEducationData());
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Harvard">
</div>
<div>
<input type="text" name="courseName" value="Web Development">
</div>
<div>
<input type="month" name="startDate" value="2000-11">
</div>
<div>
<input type="month" name="endDate" value="2008-11">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Oxford">
</div>
<div>
<input type="text" name="courseName" value="Business Management">
</div>
<div>
<input type="month" name="startDate" value="2009-10">
</div>
<div>
<input type="month" name="endDate" value="2010-05">
</div>
</div>
</div>
</form>
You can try FormData.getAll() and iterate over each group entry.
const institutionNames = formData.getAll('institutionName');
const courseNames = formData.getAll('courseName');
...
const educations = [];
for (let i = 0; i < institutionNames.length; i++) {
educations.push({
institutionName: institutionNames[i],
courseName: courseNames[i],
...
});
}
This is also a way to populate your desired format data.
$(document).ready(function(){
$(":button").click(function(){
var educations=$("#formCV .education");
var data=[];
educations.each(function(i,education){
var set={}
$(education).find(":input").each(function(i,value){
set[$(value).attr("name")] = $(value).val();
});
data.push(set);
})
console.log("data",data)
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
<input type="button" value="click me"/>
</form>
</body>
</html>
I made a JQuery function to check for empty required fields inside a closed custom dropdown.
If a required field is empty inside one of the dropdown and if the dropdown is currently closed I want the dropdown to open and if there are no empty values in the required fields I want the dropdown to close.
The problem is that the required fields aren't accessible if the dropdowns are closed and I tried to fix that problem with this function.
For some reason, it only checks for these input fields if the form is submitted at least once and the required fields are opened at least once.
find(':input[required]') doesn't give any output if the dropdown isn't opened at least once, once u open and close the dropdown the function works.
This is the function:
function dropdown_required() {
var required = 0;
$('#visible_fields').find(':input[required]').each(function () {
if (!this.value) {
for (var i = 1; i < 15; i++) {
$('.form_' + i).find(':input[required]').each(function () {
$(this).prop('required', false);
});
}
required++;
}
});
if (required == 0) {
for (var i = 1; i < 15; i++) {
var empty = 0;
$('.form_' + i).find(':input[required]').each(function ()
{
if(!this.value) {
empty++;
}
});
if (empty !== 0) {
if ($(".arrow_" + i).hasClass("rotate_2")) {
$(".arrow_" + i).addClass("rotate_1").removeClass("rotate_2");
$(".form_" + i).fadeToggle();
}
} else if ($(".arrow_" + i).hasClass("rotate_1")) {
$(".arrow_" + i).addClass("rotate_2").removeClass("rotate_1");
$(".form_" + i).fadeToggle();
}
}
}
}
This is the html:
<form method="POST" autocomplete="off" enctype="multipart/form-data" target="_self"
action="/contacten/leveranciers/iframe{{ ($leverancier == null ? '' : '/' . $leverancier->cot_id) }}">
{{ csrf_field() }}
<div id="visible_fields">
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label for="organisatie">Organisatie</label>
<input type="text" name="organisatie" id="organisatie" blocked=",;()/" hk="a"
value="{{ ($leverancier == null ? old('organisatie') : $leverancier->cot_organisatie) }}"
class="form-control inputblocked">
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="postcode">Postcode</label>
<input type="text" name="postcode" id="postcode" filter="a-zA-Z0-9" maxlength="6"
value="{{ ($leverancier == null ? old('postcode') : $leverancier->cot_postcode) }}"
class="form-control inputfilter filter_postcode">
</div>
</div>
</div>
//all visible input fields outside of the dropdowns
</div>
<label class="toggle_1">Controles<span class="arrow_1 glyphicon glyphicon-menu-left"
aria-hidden="true"></span></label>
<div class="form_1">
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label for="bkr">BKR</label>
<select name="bkr" class="form-control" required>
<option selected hidden></option>
<option value="10">BKR toetsing open</option>
<option value="11">BKR toetsing accoord</option>
<option value="12">Vrijgesteld van BKR toetsing</option>
</select>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="bkr_bestand">BKR bestand</label>
<input type="file" name="bkr_bestand" id="bkr_bestand"
data-default-file=""
class="form-control dropify">
<input type="hidden" name="verwijder_foto" class="verwijder_foto" value="0">
</div>
</div>
</div>
</div>
<div class="form-group">
<input type="hidden" id="input_iframe" name="input_iframe" value="">
<button type="submit" onclick="dropdown_required()"
class="btn btn-primary">Toevoegen </button>
</div>
</form>
</div>
</body>
</html>
Your function checks if your arrow element has the class rotate_2. The code you pasted has neither rotate_1 or rotate_2 and no else block, so the toggle never executes.
Problem demonstration:
// This group has empty mandatory elements
var empty = 1;
$('#validate').click(function() {
if (empty !== 0) {
console.log("I have empty elements!");
// From your comments, this might be backwards
if ($(".arrow_1").hasClass("rotate_2")) {
console.log("I'm going to show them");
$(".arrow_1").addClass("rotate_1").removeClass("rotate_2");
$(".form_1").fadeToggle();
}
// This is missing in the code
else {
console.log("I wasn't invited to the party");
}
// -------
} else if ($(".arrow_1").hasClass("rotate_1")) {
console.log("I'm out, I don't have empty elements...");
$(".arrow_1").addClass("rotate_2").removeClass("rotate_1");
$(".form_1").fadeToggle();
}
});
$('#simulate').click(function() {
// Simulates manually opening and closing
// In short, add rotate_2 class as if it's been toggled
$('.arrow_1').addClass('rotate_2');
console.log("Toggled manually");
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label class="toggle_1">Controles<span class="arrow_1 glyphicon glyphicon-menu-left"
aria-hidden="true"></span></label>
<div class="form_1">
<div>Some form elements</div>
</div>
<button id="validate">Validate</button>
<button id="simulate">Simulate</button>
I have two textboxes and one button,
I want to add one new textfield, that should show card name from textbox1 and Link URL append from textbox2 when I click on button
//AddnNewCardNavigator
var counter=2;
var nmecardtxt= document.getElementById("textbox1").value;
var linkurltxt= document.getElementById("textbox2").value;
$("#addbutton").click(function(){
if(nmecardtxt ==""||nmecardtxt ==0||nmecardtxt ==null
&& linkurltxt ==""||linkurltxt ==""|| linkurltxt ==0||linkurltxt ==null){
alert("Please insert value in Card name and Link Url textboxes and must be correct");
return false;
}
var NewCarddiv = $(document.createElement('div')).attr("id",'cardlink'+counter);
NewCarddiv.after().html()
})
</script>
<!-- text boxes-->
<div class="row">
<div class="col-md-12">
<div id="textboxesgroup">
<div id="textboxdiv1">
<label style="color:blanchedalmond">Card Name: </label><input type="textbox" id="textbox1">
</div>
<div id="textboxdiv2">
<label style="color:blanchedalmond">Link Url: </label><input type="textbox" id="textbox2">
</div>
</div>
</div>
</div>
Your variables nmecardtxt and linkurltxt must be created inside the click function,
because it's empty at the loading of the page.
I also took the liberty to use jQuery for that variables, as you're already using it, and tried to enhance some other things:
(See comments in my code for details)
//AddnNewCardNavigator
var counter = 2;
// On click function
$("#addbutton").click(function() {
// Here it's better
var nmecardtxt = $("#textbox1").val();
var linkurltxt = $("#textbox2").val();
// Modified you test here
if (!nmecardtxt || !linkurltxt) {
alert("Please insert value in Card name and Link Url textboxes and must be correct");
return false;
}
// Modified creation of the card
var link = $(document.createElement('a')).attr("href", linkurltxt).html(linkurltxt);
var NewCarddiv = $(document.createElement('div')).attr("id", 'cardlink' + counter).html(nmecardtxt + ": ").append(link);
$('#cards').append(NewCarddiv);
//NewCarddiv.after().html(); // Was that line an attempt of the above ?
});
body {
background: #888;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- text boxes-->
<div class="row">
<div class="col-md-12">
<div id="textboxesgroup">
<div id="textboxdiv1">
<label style="color:blanchedalmond">Card Name: </label><input type="textbox" id="textbox1">
</div>
<div id="textboxdiv2">
<label style="color:blanchedalmond">Link Url: </label><input type="textbox" id="textbox2">
</div>
</div>
</div>
</div>
<!-- Added the below -->
<div id="cards">
</div>
<button id="addbutton">Add…</button>
Hope it helps.
Here's a simplified version of what you're trying to accomplish:
function addNewCard() {
var name = $('#name').val();
var url = $('#url').val();
var count = $('#cards > .card').length;
if (!name || !url) {
alert('Missing name and/or URL.');
}
var card = $('<div class="card"></div>').html("Name: " + name + "<br>URL: " + url);
$("#cards").append(card);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<label for="url">URL:</label>
<input type="text" id="url" name="url">
<input type="submit" value="Add Card" onclick="addNewCard();">
<div id="cards">
</div>