Multi-step form with onSubmit handler (pure JavaScript)? - javascript
I am playing around with some code from the internet to try and create a mock dog walking appointment scheduling application. So far I have my multi-step form which works as should, however I have been trying to move on to starting the submit handling, and realised that as the 'next' button is changed to 'submit' (innerHTML), in the JavaScript, I am not sure where to put the onSubmit() handling functionality..
The challenge is that I am not allowed to use any server side programming, only HTML, CSS, JS and jQuery. Handling the form seems straight-forward enough but I am unsure where to implement the onSubmit() function.
Please go easy on me, it is a university challenge and JS is not my strong point, I have tried looking online but the only suggestions I have are for putting the onSubmit into the button itself, which would be the obvious option but as it's a multi-step form the submit button is not coded into the HTML.
https://codepen.io/caitlinmooneyx/pen/PoGqMaG
HTML
<form id="regForm" name="regForm" action="" class="col-sm-6">
<div class="tab">
<h3>Book your dog walk now</h3>
<!-- BOOKING FORM -->
<div class="row">
<p>Tell us about yourself first..</p>
</div>
<div class="row">
<input type="text" id="fname" placeholder="First Name" name="fname" required>
<input type="text" id="lname" placeholder="Last Name" name="lname" required>
</div>
<div class="row">
<input type="number" id="number" placeholder="Contact Number" name="Number" required>
<input type="email" id="email" placeholder="Email Address" name="email">
</div>
<div class="row">
<p>When should we pick your dog up?</p>
</div>
<div class="row">
<input type="date" id="sdate" class="datepicker" name="sdate" onchange="checkStartDate()" required>
<select name="stime" id="stime" required>
<option value="" selected disabled hidden>Choose a time</option>
<option value="nine">9:00</option>
<option value="hnine">9:30</option>
<option value="ten">10:00</option>
<option value="hten">10:30</option>
<option value="eleven">11:00</option>
<option value="heleven">11:30</option>
<option value="twelve">12:00</option>
<option value="htwelve">12:30</option>
<option value="one">13:00</option>
<option value="hone">13:30</option>
<option value="two">14:00</option>
<option value="htwo">14:30</option>
<option value="three">15:00</option>
</select>
<select name="duration" id="duration" required>
<option value="" selected disabled hidden>Duration</option>
<option value="halfhour">30 mins</option>
<option value="onehour">1 hour</option>
<option value="onehalfhour">1.5 hours</option>
<option value="twohour">2 hours</option>
</select>
</div>
<div class="row">
<p>Where should we pick up/drop off your dog?</p>
</div>
<div class="row">
<div id="locationField">
<input
id="autocomplete"
placeholder="Start typing your address..."
onFocus="geolocate()"
type="text"
required
/>
</div>
</div>
</div>
<div class="tab">
<h3>Now tell us more about your dog..</h3>
<div class="row">
<input type="text" id="dogname" placeholder="Dog Name" name="dogname" required>
</div>
<div class="row">
<p>What breed are they?</p>
</div>
<div class="row">
<select class="breeds"></select>
</div>
<div class="row">
<p>Their favourite places?</p>
</div>
<div class="row">
<input type="checkbox" id="parks" name="parks" value="Parks">
<label for="parks"> Parks</label><br>
<input type="checkbox" id="forests" name="forests" value="Forests">
<label for="forests"> Forests</label><br>
<input type="checkbox" id="beaches" name="beaches" value="Beaches">
<label for="beaches"> Beaches</label>
</div>
<div class="row">
<p>Anything else we should know? (Favourite toys, places to avoid etc)</p>
</div>
<div class="row">
<textarea></textarea>
</div>
</div>
<div style="overflow:auto;">
<div style="float:right;">
<button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button>
<button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button>
</div>
</div>
<div style="text-align:center;margin-top:40px;">
<span class="step"></span>
<span class="step"></span>
</div>
</form>
CSS
form p {
margin: 1em 0 .5em 1em;
}
form {
background-color: #fafafa;
padding: 1.5em;
/*margin-right: 6em;*/
}
input, select, textarea {
margin: 1em;
padding: .5em;
width: 45%;
font-size: 17px;
border: 1px solid #aaa;
}
input[type="checkbox"] {
width: auto;
margin: .5em 1em 0 1em;
}
input[type="date"] {
color: #aaa!important;
}
#locationField > input {
width: 290%!important;
}
input.invalid {
background-color: #ffdddd;
}
.tab {
display: none;
}
.step {
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbbbbb;
border: none;
border-radius: 50%;
display: inline-block;
opacity: 0.5;
}
.step.active {
opacity: 1;
background-color: #fac123;
}
JS
// ~~~ tab functionality
var currentTab = 0; // current tab is set to be the first tab (0)
showTab(currentTab); // display the current tab
function showTab(n) {
var x = document.getElementsByClassName("tab");
x[n].style.display = "block";
if (n == 0) {
document.getElementById("prevBtn").style.display = "none";
} else {
document.getElementById("prevBtn").style.display = "inline";
}
if (n == (x.length - 1)) {
document.getElementById("nextBtn").innerHTML = "Submit";
} else {
document.getElementById("nextBtn").innerHTML = "Next";
}
fixStepIndicator(n)
}
function nextPrev(n) {
var x = document.getElementsByClassName("tab");
if (n == 1 && !validateForm()) return false;
x[currentTab].style.display = "none";
currentTab = currentTab + n;
if (currentTab >= x.length) {
//...the form gets submitted:
document.getElementById("regForm").submit();
return false;
}
showTab(currentTab);
}
function validateForm() {
var x, y, i, s, valid = true;
x = document.getElementsByClassName("tab");
y = x[currentTab].getElementsByTagName("input");
s = x[currentTab].getElementsByTagName("select");
for (i = 0; i < y.length; i++) {
if (y[i].value == "") {
y[i].className += " invalid";
valid = false;
}
}
for (i = 0; i < s.length; i++) {
if (s[i].value == "") {
s[i].className += " invalid";
valid = false;
}
}
if (valid) {
document.getElementsByClassName("step")[currentTab].className += " finish";
}
return valid; // return the valid status
}
function fixStepIndicator(n) {
var i, x = document.getElementsByClassName("step");
for (i = 0; i < x.length; i++) {
x[i].className = x[i].className.replace(" active", "");
}
x[n].className += " active";
}
// ~~~ dog breed selector
const BREEDS_URL = 'https://dog.ceo/api/breeds/list/all';
const select = document.querySelector('.breeds');
fetch(BREEDS_URL)
.then(res => {
return res.json();
})
.then(data => {
const breedsObject = data.message;
const breedsArray = Object.keys(breedsObject);
for (let i = 0; i < breedsArray.length; i++) {
const option = document.createElement('option');
option.value = breedsArray[i];
option.innerText = breedsArray[i];
select.appendChild(option);
}
console.log(breedsArray);
});
// ~~~ basic form validation
// ~~~ date validation
function checkStartDate() {
var startDate = document.getElementById('sdate').value;
var selectedStartDate = new Date(startDate);
var now = new Date();
if (selectedStartDate < now) {
alert("Start date must be in the future");
$("#sdate").addClass("invalid");
}
}
One solution could be to have a submit button that you hide by default (on the first tab) and show once you go to the second (or last if you add more). This way you won't have to change the innerHTML on any element and just toggle a class. Something like that could look like:
<form id="regForm" name="regForm" action="[NEED ACTION]" class="col-sm-6">
...
<div style="overflow:auto;">
<div style="float:right;">
<button type="button" id="prevBtn" click="nextPrev(-1)">Previous</button>
<button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button>
<submit type="submit" class="hide" value="Submit" />
</div>
</div>
</form>
function showTab(n) {
var x = document.getElementsByClassName("tab");
x[n].style.display = "block";
if (n == 0) {
document.getElementById("prevBtn").style.display = "none";
} else {
document.getElementById("prevBtn").style.display = "inline";
}
if (n == (x.length - 1)) {
document.getElementById("nextBtn").classList.add('hide');
document.getElementById("submitBtn").classList.remove('hide');
} else {
document.getElementById("nextBtn").classList.remove('hide');
document.getElementById("submitBtn").classList.add('hide');
}
fixStepIndicator(n)
}
For this to work you will need to fill in the action property for the form.
Another way you could do this without adding an extra element would be to change the onClick action for the next/submit button.
function showTab(n) {
var x = document.getElementsByClassName("tab");
var nextSubmitBtn = document.getElementById("nextBtn");
x[n].style.display = "block";
if (n == 0) {
document.getElementById("prevBtn").style.display = "none";
} else {
document.getElementById("prevBtn").style.display = "inline";
}
if (n == (x.length - 1)) {
nextSubmitBtn.textContent = "Submit";
nextSubmitBtn.onClick = someSubmitFunc;
} else {
nextSubmitBtn.textContent = "Next";
nextSubmitBtn.onClick = function () { nextPrev(1); };
}
fixStepIndicator(n)
}
This would allow you to keep the same HTML and handle the solution via JS. If you do this remember to keep the current onClick property on the "next" button as that will be the initial function ran (when you first click "next")
A couple tips and notes:
If you are only changing the text of an element (like from "next" to "submit") it would be best to use a function that only changes the text:
// Pure JS
element.textContnet = 'some text';
// jQuery
$element.text('some other text');
this will help prevent possible bugs (and potentially security risk) that can come from innerHTML.
You say you are using jQuery but it is only used in one line in the JS code presented. If that is the only line using jQuery library you could easily replace it and not include the library saving on you site size.
// jQuery way to add class (line 111)
$("#sdate").addClass("invalid");
// Pure JS equivalent
document.getElementById('sdate').classList.add('invalid');
Both will get the job done (add a class) and if you prefer to use jQuery more power to you but if that is the only place you use it then this could be an alternative.
Related
Add fileupload input fields inside multiple div at a same time according to radiobox selection
I have a input field that takes numeric inputs. Then i have a button which display the number of divs as per that input. after displaying div there is two radio-box buttons (paired end and single end) if I select paired end then i want two file upload fields in each divs. and if i select single end then i want only one file upload fields in each div. I have tried but fileupload fields working on only first div. function CreateText() { var text = `<div class="row border-top py-3"> <div class="col-md-3"> <label">sample name *</label></br> <input type="sample" id="sample" name="sample[]"> </div> <div class="col-md-3" style="display:none" id="showsingle"> <div class="form-group"> <label for="form_upload">Upload file *</label></br> <input type="file" id="myFile" name="filename1[]"> </div> </div> <div class="col-md-3" style="display:none" id="showpair"> <div class="form-group"> <label for="form_upload">Upload file *</label></br> <input type="file" id="myFile" name="filename2[]"> <label for="form_upload">Upload file *</label></br> <input type="file" id="myFile" name="filename2[]"> </div> </div> <div class="col-md-3 d-grid"> <div class="form-group"> <button class="btn btn-danger remove_add_btn">Remove</button> </div> </div> </div>`; var textCount = document.getElementById('textInput').value; var html = ''; for (var i = 0; i < $('#textInput').val(); i++) { html = document.getElementById('divDynamicTexts').innerHTML; document.getElementById('divDynamicTexts').innerHTML = html + text.replace('', i); } } function onlyOne() { let SradioBox = document.getElementById("singleradio"), Sfileupload = document.getElementById("showsingle"), PradioBox = document.getElementById("pairedradio"), Pfileupload = document.getElementById("showpair"); if (SradioBox.checked == true) { Sfileupload.style.display = "block", Pfileupload.style.display = "none"; } else if (PradioBox.checked == true) { Pfileupload.style.display = "block", Sfileupload.style.display = "none"; } else { Pfileupload.style.display = "none", Sfileupload.style.display = "none"; } }; $(document).ready(function() { $(document).on('click', '.remove_add_btn', function(e) { e.preventDefault(); let row_item = $(this).parent().parent().parent(); $(row_item).remove(); }); }); <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="text-center"> <input type="text" id="textInput" value="" /> <input type="button" id="" value="Create upload fields" onclick="CreateText();" /> <div class="col-md-4" id="filebutton"> <div class="form-group "> <label for="form_need">Library Type *</label> </br> <div class="px-2"> <label for="myradio">Single end:</label> <input type="radio" id="singleradio" name="check" onclick="onlyOne();"> <label for="myradio">Paired end:</label> <input type="radio" id="pairedradio" name="check" onclick="onlyOne();"> </div> </div> </div> </div> <div id="divDynamicTexts"></div>
ID attributes must be unique. It would be better to remove the IDs altogether ( or change to dataset attributes perhaps ) and use a delegated event listener to process the various clicks related to the task of adding/removing dynamic elements. In the code below all ID attributes were either removed entirely or changed to data-id type values. To avoid the need to process different form file input fields at the server the file-input fields are named the same but have an index so can be identified more readily in PHP ( or whatever backend you have ) The delegated listener, because it is bound to the document, will work for all elements whether or not they are static or added dynamically and makes heavy use of the event.target property to help identify the element that invoked the event. The label element was being used incorrectly previously. If the form-input is within the label then there is no need for the for="ID" syntax ( note that the ID should be the ID of the input element to which the label belongs! ) - as it was the label's appeared to have a for attribute which did not related to an element in the form! Using querySelector and querySelectorAll you can easily identify nodes of interest within the DOM so button clicks or radio button selection can fire a query to find nodes that are relevant - thus simplifying the hiding/showing of the file input elements. const strhtml = ` <div data-id="dynrow" class="row border-top py-3"> <div class="col-md-3"> <label>sample name *<input type="text" name="sample[]"></label> </div> <div class="col-md-3" style="display:none" data-id="single" data-role="file-field"> <div class="form-group"> <label>Upload file *<input type="file" name="filename[1]" /></label> </div> </div> <div class="col-md-3" style="display:none" data-id="pair" data-role="file-field"> <div class="form-group "> <label>Upload file *<input type="file" name="filename[1]" /></label> <label>Upload file *<input type="file" name="filename[2]" /></label> </div> </div> <div class="col-md-3 d-grid"> <div class="form-group"> <button class="btn btn-danger remove_add_btn" data-id='remove'>Remove</button> </div> </div> </div>`; const _radio = document.querySelectorAll('[type="radio"][data-id]'); const _bttn = document.querySelector('[type="button"][data-id="add"]'); const _div = document.querySelector('#divDynamicTexts'); const _input = document.querySelector('input[type="number"][data-id="textInput"]'); let choice = false; let qty = false; /* Disable radio buttons and the "Create" button initially and enable when changes are made in the correct sequence. 1: select quantity -> enable radio bttns 2: select single or double -> enable "create" bttn 3: click bttn, create as per radio selection */ _input.addEventListener('change', e => { _radio.forEach(n => { n.disabled = e.target.value > 0 ? false : true; }); qty=e.target.value; }); document.addEventListener('click', e => { if (e.target instanceof HTMLInputElement && e.target.dataset.id != null) { /* set global "choice" variable and enable "Create" bttn. */ if (e.target.type == 'radio') { choice = e.target.dataset.id; _bttn.disabled = false; } } /* If the "choice" has been made the radio buttons will be enabled. Based on radio button selected create new HTML and then unhide the appropriate single/pair DIV element */ if (choice && qty > 0 && e.target.type == 'button') { _div.innerHTML = ''; for (let i = 0; i < qty; i++) _div.insertAdjacentHTML('afterbegin', strhtml); let expr = `div[data-id="${choice}"]`; document.querySelectorAll(expr).forEach(n => n.style.display = 'block'); } /* unchanged: delete DIV & contents when "Remove" bttn is clicked. */ if (e.target instanceof HTMLButtonElement && e.target.dataset.id != null) { if (e.target.dataset.id == 'remove') { _div.removeChild(e.target.closest('[data-id="dynrow"]')); } } }); body { font-family: arial; } label { display: block; } .px-2 { display: inline-block; } .px-2 label { display: inline-block; margin: 0.5rem; } h2 { font-size: 1.1rem; margin: 1rem 0 0 0; display: inline-block; width: auto; } .inline { display: inline; margin: 0 1rem 0 0; } #divDynamicTexts { min-height: 1rem; margin: 2rem auto } div.row { padding: 0.5rem; border: 1px dotted grey; margin: 0.5rem; } div[data-id='single'] .form-group label { background: aliceblue } div[data-id='pair'] .form-group label { background: lightsteelblue } div[data-id] .form-group label { outline: 1px solid grey; padding: 0.5rem; margin: 0.5rem 0 } .bold { font-weight: bold } [disabled]{ border:1px solid red; outline:2px solid red; background:rgba(255,0,0,0.25); } <div class='text-center'> <label class='inline bold'>Quantity:<input type='number' data-id='textInput' /></label> <div class='col-md-4'> <div class='form-group'> <h2>Library Type</h2> <div class='px-2'> <label>Single end: <input type='radio' data-id='single' name='check' disabled /></label> <label>Paired end: <input type='radio' data-id='pair' name='check' disabled /></label> </div> </div> </div> <input type='button' data-id='add' value='Create upload fields' disabled /> </div> <div id='divDynamicTexts'></div>
Error messages for form validation not appearing
I am currently building a window on a website where users can book a boat trip, which requires multiple steps, and 3/5 steps include forms (I only included the first form in the html since it would get too long otherwise - see below). I am currently handling the validation of the first form, which you can see below (".availability step1"). I've spent quite some time on this validation, however, I can't seem to figure out how to make only the "empty", so the fields that are not valid, take on the error message (.error). Right now it is recognising the ones that are invalid, and I'm getting the CSS connected to invalid (I am getting the red border around the input field), however, I am not getting through the html tag, which is a paragraph that goes underneath the input field. function init() { setUpBooking(); } function setUpBooking(){ formValidation(); } function formValidation() { /* ------------ form & elements ----------- */ const form1 = document.querySelector(".availability"); window.form1 = form1; const elements = form1.elements; window.elements = elements; /* --------- delete default validation ------- */ form1.setAttribute("novalidate", true); /* ------------ custom validation ------------ */ document.querySelector(".next").addEventListener("click", (e) => { e.preventDefault(); // 1. select all inputs const formElements = form1.querySelectorAll("input, select"); /* ------------ date ------------ */ if (form1.checkValidity()) { console.log("form is valid"); // loop through form elements and check if are valid or not formElements.forEach((el) => { if (el.checkValidity()) { el.classList.add("valid"); } // enable "next" btn when form is valid var counter = 1, step = "step"; step = ".step" + counter; if (counter <= 5) { document.querySelector(step).classList.add("show"); } counter++; if (counter > 5) { counter = 5; } step = ".step" + counter; // step is the class and we are appending counter with step so that it looks like the same class in the given class(like counter 1 means step1) document.querySelector(step).classList.remove("show"); // enable "previous" btn when form is valid document.querySelector(".previous").addEventListener('click', function () { if (counter > 1) { // we don't want to remove the first step, it will always be shown step = ".step" + counter; document.querySelector(step).classList.add("show"); } counter--; if (counter < 1) { counter = 1; } step = ".step" + counter; document.querySelector(step).classList.remove("show"); }); }); } else { formElements.forEach((el) => { if (!el.checkValidity()) { console.log("form is invalid"); el.classList.add("invalid"); document.querySelector(".error").style.display = "block"; } else { el.classList.remove("invalid"); } }) } }) } .valid { border: 1px solid green; } .invalid { border: 1px solid red; border-top-left-radius: 3px; border-top-right-radius: 3px; } .error { text-transform: initial; margin-bottom: 20px; margin-top: -1px; border: 1px solid red; padding: 4px; z-index: 10; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; display: none; } <!-- AVAILABILITY --> <form class="availability step1"> <label for="date">Choose a date <input type="date" required> <p class="error date-err">Please pick a date for your tour</p> <label for="number">Choose passengers <input type="number" required> <p class="error passengers-err">Please pick a number of passengers</p> </label> <!-- PERSONAL DATA --> <form class="personalData step2"> </form> <!-- ORDER OVERVIEW --> <div class="orderOverview step3"> </div> <!-- PAYMENT --> <form class="payment step4"> </form> <!-- buttons --> <button class="previous hide">Previous</button> <button class="next">Next</button>
Firstly, sorry. I've made some modifications to your forms. Ideas are as follows: Appended a span field in every form. Before going to the next form just check every field is filled in the current form by passing the form number to the validateForm function and check all its input fields are filled. If yes, return true else return false. Have a look at the snippet below: function validateForm(step) { // console.log(document.forms[step - 1].elements); var i, l = document.forms[step - 1].elements.length; for (i = 0; i < l; i++) { // console.log(document.forms[step - 1].elements[i].value); if (!document.forms[step - 1].elements[i].value) { // console.log("All fields should be filled"); document.getElementById("error" + step).textContent = "Fill all the fields please"; document.getElementById("error" + step).style.color = "red"; return false; } } document.getElementById("error" + step).textContent = "Form is completed"; document.getElementById("error" + step).style.color = "green"; return true; } var counter = 1, step = "step"; document.querySelector(".next").addEventListener('click', function() { step = ".step" + counter; if (validateForm(counter)) { if (counter <= 5) { document.querySelector(step).classList.add("show"); } counter++; if (counter > 5) { counter = 5; } step = ".step" + counter; // step is the class and we are appending counter with step so that it looks like the same class in the given class(like counter 1 means step1) //console.log(step); document.querySelector(step).classList.remove("show"); } }); document.querySelector(".previous").addEventListener('click', function() { if (counter > 1) { // we don't want to remove the first step, it will always be shown step = ".step" + counter; //console.log(step); document.querySelector(step).classList.add("show"); } counter--; if (counter < 1) { counter = 1; } step = ".step" + counter; document.querySelector(step).classList.remove("show"); }); .show { display: none; } <!-- AVAILABILITY --> <form name="availability" class="availability step1"> <h1>Step1</h1> <label for="date">Choose a date</label> <input type="date" name="DATE" required> <label for="firstname">Enter a firstname</label> <input type="text" name="FIRSTNAME" required> <br/> <span id="error1"> </span> </form> <!-- PERSONAL DATA --> <form class="personalData step2 show"> <h1>Step2</h1> <label for="date">Choose a date</label> <input type="date" name="DATE" required> <label for="firstname">Enter a firstname</label> <input type="text" name="FIRSTNAME" required> <br/> <span id="error2"></span> </form> <!-- ORDER OVERVIEW --> <div class="orderOverview step3 show"> <h1>Step3</h1> <label for="date">Choose a date</label> <input type="date" name="DATE" required> <label for="firstname">Enter a firstname</label> <input type="text" name="FIRSTNAME" required> <br/> <span id="error3"></span> </div> <!-- PAYMENT --> <form class="payment step4 show"> <h1>Step4</h1> <label for="date">Choose a date</label> <input type="date" name="DATE" required> <label for="firstname">Enter a firstname</label> <input type="text" name="FIRSTNAME" required> <br/> <span id="error4"></span> </form> <!-- buttons --> <button class="previous hide">Previous</button> <button class="next">Next</button>
Apply Google Apps Script Array into HTML form autocomplete datalist
I have managed to make a Google Scripts function that creates an array from a Google Sheet column filtering out any spaces. However, I'm not sure how to pass this variable to my HTML form so that I can create an autocomplete options list. Users will be adding content to spreadsheet so the list has to be dynamic to grow with the spreadsheet. Forms user should be able to type with autocomplete or scroll from the list of items in the spreadsheet array that will change over time. Edit: sorry for any confusion by not showing enough code. Basically I'm trying to get the array in readData() to turn into something like this in html <datalist id="datalist"> <option value="Plain#1"> <option value="Plain#2"> <option value="Plain#3"> <option value="Plain#4"> <option value="Plain#5"> <option value="Plain#6"> </datalist> Code follows: Here is my JS <script> // Prevent forms from submitting. function preventFormSubmit() { var forms = document.querySelectorAll('form'); for (var i = 0; i < forms.length; i++) { forms[i].addEventListener('submit', function(event) { event.preventDefault(); }); } } window.addEventListener('load', preventFormSubmit); function handleFormSubmit(formObject) { google.script.run.processForm(formObject); document.getElementById("myForm").reset(); } </script> Here is my GS function readData() { var url = "https://docs.google.com/spreadsheets/d/1ftdM8esHXRbdNbN0I00GSMPj5ZiCcPo9cs_9dAjFldc/edit#gid=540792552;" var ss = SpreadsheetApp.openByUrl(url); var ws = ss.getSheetByName("Bags Inventory"); var rng = ws.getRange(2,2,350,1) var rangeArray = rng.getValues(); var filArray = rangeArray.filter(function (el) { return el != ""; }); return(filArray); } function doGet(request) { return HtmlService.createTemplateFromFile('Index').evaluate(); } /* #Include JavaScript and CSS Files */ function include(filename) { return HtmlService.createHtmlOutputFromFile(filename) .getContent(); } /* #Process Form */ function processForm(formObject) { var url = "https://docs.google.com/spreadsheets/d/1PRS5O42_vAwEqLFjULu-SyPvzSR902tu9xMXA88WnWM/edit#gid=73972270;" var ss = SpreadsheetApp.openByUrl(url); var ws = ss.getSheetByName("Data"); ws.appendRow([ new Date(), Session.getActiveUser().getEmail(), formObject.item1, formObject.amount1, formObject.item2, formObject.amount2, formObject.item3, formObject.amount3, formObject.item4, formObject.amount4, formObject.item5, formObject.amount5, formObject.item6, formObject.amount6, formObject.item7, formObject.amount7, formObject.item8, formObject.amount8, formObject.item9, formObject.amount9, formObject.item10, formObject.amount10, formObject.client]); } Here is my HTML <!DOCTYPE html> <html> <head> <base target="_top"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> <style> ::placeholder{ color: #D3D3D3 !important; } </style> <?!= include('JavaScript'); var options = readData(); ?> </head> <body> <div class="container"> <div class="row"> <div class="col-6"> <form id="myForm" onsubmit="handleFormSubmit(this)"> <p class="h4 mb-4 text-center">Paper Bag Inventory</p> <div class="form-row"> <div class="form-group col-md-6"> <label for="item1">Item Code</label> <input type="text" class="form-control" id="item1" name="item1" placeholder="Item #1" list="datalist" required> </div> <div class="form-group col-md-6"> <label for="amount1">Amount</label> <input type="text" class="form-control" id="amount1" name="amount1" placeholder="Amount" required> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item2" name="item2" placeholder="Item #2" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount2" name="amount2" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item3" name="item3" placeholder="Item #3" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount3" name="amount3" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item4" name="item4" placeholder="Item #4" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount4" name="amount4" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item5" name="item5" placeholder="Item #5" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount5" name="amount5" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item6" name="item6" placeholder="Item #6" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount6" name="amount6" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item7" name="item7" placeholder="Item #7" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount7" name="amount7" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item8" name="item8" placeholder="Item #8" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount8" name="amount8" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item9" name="item9" placeholder="Item #9" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount9" name="amount9" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="item10" name="item10" placeholder="Item #10" list="datalist"> </div> <div class="form-group col-md-6"> <input type="text" class="form-control" id="amount10" name="amount10" placeholder="Amount"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <input type="text" class="form-control" id="client" name="client" placeholder="Client Name" required> </div> </div> <button type="submit" class="btn btn-primary btn-block">Submit</button> </form> <div id="output"></div> </div> </div> </div> </body> </html> <datalist id="datalist"> <script type="text/javascript"> // my attempt to call gs function var mylist = readData() var options = ''; for(var i = 0; i < mylist.length; i++) options += '<option value="'+mylist[i]+'" />'; document.getElementById('datalist').innerHTML = options; </script> </datalist>
I would like to propose the following modification. Modification points: In your case, I would like to recommend to return 1 dimensional array from readData() at Google Apps Script. From your HTML&Javascript, I think that your HTML file is put to the Google Apps Script project. From this situation, I would like to propose to put the values from the Spreadsheet using the templated HTML. When above points are reflected to your script, it becomes as follows. Modified script: Google Apps Script side: function readData() { var url = "https://docs.google.com/spreadsheets/d/1ftdM8esHXRbdNbN0I00GSMPj5ZiCcPo9cs_9dAjFldc/edit#gid=540792552;" var ss = SpreadsheetApp.openByUrl(url); var ws = ss.getSheetByName("Bags Inventory"); var rng = ws.getRange(2,2,350,1) var rangeArray = rng.getValues(); var filArray = rangeArray.filter(function (el) {return el[0] != ""}).flat(); // Modified return filArray; } And, unfortunately, I couldn't understand the method for using the HTML in your situation. So if you are using your script as the Web Apps, please modify as follows. function doGet() { var html = HtmlService.createTemplateFromFile("index"); html.data = readData(); return html.evaluate(); } If you use sidebar, please use this. var html = HtmlService.createTemplateFromFile("index"); html.data = readData(); SpreadsheetApp.getUi().showSidebar(html.evaluate()); If you use dialog, please use this. var html = HtmlService.createTemplateFromFile("index"); html.data = readData(); SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "sample"); HTML&Javascript side: From: <datalist id="datalist"></datalist> To: <datalist id="datalist"> <? for (var i = 0; i < data.length; i++) { ?> <option value="<?= data[i] ?>"> <? } ?> </datalist> Note: When you use the Web Apps and you modified the script, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this. Reference: HTML Service: Templated HTML
Do you want a autocomplete input form work like this? function autocomplete(inp, arr) { /*the autocomplete function takes two arguments, the text field element and an array of possible autocompleted values:*/ var currentFocus; /*execute a function when someone writes in the text field:*/ inp.addEventListener("input", function(e) { var a, b, i, val = this.value; /*close any already open lists of autocompleted values*/ closeAllLists(); if (!val) { return false;} currentFocus = -1; /*create a DIV element that will contain the items (values):*/ a = document.createElement("DIV"); a.setAttribute("id", this.id + "autocomplete-list"); a.setAttribute("class", "autocomplete-items"); /*append the DIV element as a child of the autocomplete container:*/ this.parentNode.appendChild(a); /*for each item in the array...*/ for (i = 0; i < arr.length; i++) { /*check if the item starts with the same letters as the text field value:*/ if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) { /*create a DIV element for each matching element:*/ b = document.createElement("DIV"); /*make the matching letters bold:*/ b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>"; b.innerHTML += arr[i].substr(val.length); /*insert a input field that will hold the current array item's value:*/ b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>"; /*execute a function when someone clicks on the item value (DIV element):*/ b.addEventListener("click", function(e) { /*insert the value for the autocomplete text field:*/ inp.value = this.getElementsByTagName("input")[0].value; /*close the list of autocompleted values, (or any other open lists of autocompleted values:*/ closeAllLists(); }); a.appendChild(b); } } }); /*execute a function presses a key on the keyboard:*/ inp.addEventListener("keydown", function(e) { var x = document.getElementById(this.id + "autocomplete-list"); if (x) x = x.getElementsByTagName("div"); if (e.keyCode == 40) { /*If the arrow DOWN key is pressed, increase the currentFocus variable:*/ currentFocus++; /*and and make the current item more visible:*/ addActive(x); } else if (e.keyCode == 38) { //up /*If the arrow UP key is pressed, decrease the currentFocus variable:*/ currentFocus--; /*and and make the current item more visible:*/ addActive(x); } else if (e.keyCode == 13) { /*If the ENTER key is pressed, prevent the form from being submitted,*/ e.preventDefault(); if (currentFocus > -1) { /*and simulate a click on the "active" item:*/ if (x) x[currentFocus].click(); } } }); function addActive(x) { /*a function to classify an item as "active":*/ if (!x) return false; /*start by removing the "active" class on all items:*/ removeActive(x); if (currentFocus >= x.length) currentFocus = 0; if (currentFocus < 0) currentFocus = (x.length - 1); /*add class "autocomplete-active":*/ x[currentFocus].classList.add("autocomplete-active"); } function removeActive(x) { /*a function to remove the "active" class from all autocomplete items:*/ for (var i = 0; i < x.length; i++) { x[i].classList.remove("autocomplete-active"); } } function closeAllLists(elmnt) { /*close all autocomplete lists in the document, except the one passed as an argument:*/ var x = document.getElementsByClassName("autocomplete-items"); for (var i = 0; i < x.length; i++) { if (elmnt != x[i] && elmnt != inp) { x[i].parentNode.removeChild(x[i]); } } } /*execute a function when someone clicks in the document:*/ document.addEventListener("click", function (e) { closeAllLists(e.target); }); } /*An array containing all the country names in the world:*/ var countries = ["Afghanistan","Albania","Algeria","Andorra","Angola","Anguilla","Antigua & Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia","Bosnia & Herzegovina","Botswana","Brazil","British Virgin Islands","Brunei","Bulgaria","Burkina Faso","Burundi","Cambodia","Cameroon","Canada","Cape Verde","Cayman Islands","Central Arfrican Republic","Chad","Chile","China","Colombia","Congo","Cook Islands","Costa Rica","Cote D Ivoire","Croatia","Cuba","Curacao","Cyprus","Czech Republic","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador","Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Ethiopia","Falkland Islands","Faroe Islands","Fiji","Finland","France","French Polynesia","French West Indies","Gabon","Gambia","Georgia","Germany","Ghana","Gibraltar","Greece","Greenland","Grenada","Guam","Guatemala","Guernsey","Guinea","Guinea Bissau","Guyana","Haiti","Honduras","Hong Kong","Hungary","Iceland","India","Indonesia","Iran","Iraq","Ireland","Isle of Man","Israel","Italy","Jamaica","Japan","Jersey","Jordan","Kazakhstan","Kenya","Kiribati","Kosovo","Kuwait","Kyrgyzstan","Laos","Latvia","Lebanon","Lesotho","Liberia","Libya","Liechtenstein","Lithuania","Luxembourg","Macau","Macedonia","Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Marshall Islands","Mauritania","Mauritius","Mexico","Micronesia","Moldova","Monaco","Mongolia","Montenegro","Montserrat","Morocco","Mozambique","Myanmar","Namibia","Nauro","Nepal","Netherlands","Netherlands Antilles","New Caledonia","New Zealand","Nicaragua","Niger","Nigeria","North Korea","Norway","Oman","Pakistan","Palau","Palestine","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Poland","Portugal","Puerto Rico","Qatar","Reunion","Romania","Russia","Rwanda","Saint Pierre & Miquelon","Samoa","San Marino","Sao Tome and Principe","Saudi Arabia","Senegal","Serbia","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","Solomon Islands","Somalia","South Africa","South Korea","South Sudan","Spain","Sri Lanka","St Kitts & Nevis","St Lucia","St Vincent","Sudan","Suriname","Swaziland","Sweden","Switzerland","Syria","Taiwan","Tajikistan","Tanzania","Thailand","Timor L'Este","Togo","Tonga","Trinidad & Tobago","Tunisia","Turkey","Turkmenistan","Turks & Caicos","Tuvalu","Uganda","Ukraine","United Arab Emirates","United Kingdom","United States of America","Uruguay","Uzbekistan","Vanuatu","Vatican City","Venezuela","Vietnam","Virgin Islands (US)","Yemen","Zambia","Zimbabwe"]; /*initiate the autocomplete function on the "myInput" element, and pass along the countries array as possible autocomplete values:*/ autocomplete(document.getElementById("myInput"), countries); * { box-sizing: border-box; } body { font: 16px Arial; } /*the container must be positioned relative:*/ .autocomplete { position: relative; display: inline-block; } input { border: 1px solid transparent; background-color: #f1f1f1; padding: 10px; font-size: 16px; } input[type=text] { background-color: #f1f1f1; width: 100%; } input[type=submit] { background-color: DodgerBlue; color: #fff; cursor: pointer; } .autocomplete-items { position: absolute; border: 1px solid #d4d4d4; border-bottom: none; border-top: none; z-index: 99; /*position the autocomplete items to be the same width as the container:*/ top: 100%; left: 0; right: 0; } .autocomplete-items div { padding: 10px; cursor: pointer; background-color: #fff; border-bottom: 1px solid #d4d4d4; } /*when hovering an item:*/ .autocomplete-items div:hover { background-color: #e9e9e9; } /*when navigating through the items using the arrow keys:*/ .autocomplete-active { background-color: DodgerBlue !important; color: #ffffff; } <!--Make sure the form has the autocomplete function switched off:--> <form autocomplete="off" action="/action_page.php"> <div class="autocomplete" style="width:300px;"> <input id="myInput" type="text" name="myCountry" placeholder="Country"> </div> <input type="submit"> </form> Just replace the countries variable by your array requested from google sheet Source code by https://www.w3schools.com/howto/howto_js_autocomplete.asp
Need to add function to make sure at least 1 checkbox is selected before submitting multipage form?
I am working on a form with multiple pages. On one page I have check boxes that I would require at least one to be selected before submission. How can I do that with javascript/html5? It is all one form that changes pages with javascript. How on the last page can I have the form check, if at least one checkbox has been selected? var currentTab = 0; // Current tab is set to be the first tab (0) showTab(currentTab); // Display the current tab function showTab(n) { // This function will display the specified tab of the form... var x = document.getElementsByClassName("tab"); x[n].style.display = "block"; //... and fix the Previous/Next buttons: if (n == 0) { document.getElementById("prevBtn").style.display = "none"; } else { document.getElementById("prevBtn").style.display = "inline"; } if (n == (x.length - 1)) { document.getElementById("nextBtn").innerHTML = "Submit"; } else { document.getElementById("nextBtn").innerHTML = "Next"; } //... and run a function that will display the correct step indicator: fixStepIndicator(n) } function nextPrev(n) { // This function will figure out which tab to display var x = document.getElementsByClassName("tab"); // Exit the function if any field in the current tab is invalid: if (n == 1 && !validateForm()) return false; // Hide the current tab: x[currentTab].style.display = "none"; // Increase or decrease the current tab by 1: currentTab = currentTab + n; // if you have reached the end of the form... if (currentTab >= x.length) { // ... the form gets submitted: document.getElementById("regForm").submit(); return false; } // Otherwise, display the correct tab: showTab(currentTab); } function validateForm() { // This function deals with validation of the form fields var x, y, i, valid = true; x = document.getElementsByClassName("tab"); y = x[currentTab].getElementsByTagName("input"); // A loop that checks every input field in the current tab: for (i = 0; i < y.length; i++) { // If a field is empty... if (y[i].value == "") { // add an "invalid" class to the field: y[i].className += " invalid"; // and set the current valid status to false valid = false; } } // If the valid status is true, mark the step as finished and valid: if (valid) { document.getElementsByClassName("step")[currentTab].className += " finish"; } return valid; // return the valid status } function fixStepIndicator(n) { // This function removes the "active" class of all steps... var i, x = document.getElementsByClassName("step"); for (i = 0; i < x.length; i++) { x[i].className = x[i].className.replace(" active", ""); } //... and adds the "active" class on the current step: x[n].className += " active"; } * { box-sizing: border-box; } body { background-color: #f1f1f1; } #regForm { background-color: #ffffff; margin: 100px auto; font-family: Raleway; padding: 40px; width: 70%; min-width: 300px; } h1 { text-align: center; } input { padding: 10px; width: 100%; font-size: 17px; font-family: Raleway; border: 1px solid #aaaaaa; } /* Mark input boxes that gets an error on validation: */ input.invalid { background-color: #ffdddd; } /* Hide all steps by default: */ .tab { display: none; } button { background-color: #4CAF50; color: #ffffff; border: none; padding: 10px 20px; font-size: 17px; font-family: Raleway; cursor: pointer; } button:hover { opacity: 0.8; } #prevBtn { background-color: #bbbbbb; } /* Make circles that indicate the steps of the form: */ .step { height: 15px; width: 15px; margin: 0 2px; background-color: #bbbbbb; border: none; border-radius: 50%; display: inline-block; opacity: 0.5; } .step.active { opacity: 1; } /* Mark the steps that are finished and valid: */ .step.finish { background-color: #4CAF50; } <form id="regForm" action="/action_page.php"> <h1>Register:</h1> <!-- One "tab" for each step in the form: --> <div class="tab">Name: <p><input placeholder="First name..." oninput="this.className = ''" name="fname"></p> <p><input placeholder="Last name..." oninput="this.className = ''" name="lname"></p> </div> <div class="tab">Contact Info: <p><input placeholder="E-mail..." oninput="this.className = ''" name="email"></p> <p><input placeholder="Phone..." oninput="this.className = ''" name="phone"></p> </div> <div class="tab"> <H3 id="subhead">AUDITORIUM ROOM SETUP & EQUIPMENT REQUESTS</H3> <label><label id="asterisk">*</label>Room Setup (Select all that apply):</label><br><br> <!-- <p><input placeholder="dd" oninput="this.className = ''" name="dd"></p> --> <br> <input id="element_3_1" name="Room-Setup" required type="checkbox" value="Auditorium-Seating-for-300" /> <label class="choice" id="labelinput" for="element_3_1">Auditorium Seating for 300</label> <br> <input id="element_3_2" name="Room-Setup" class="elementcheckbox" type="checkbox" value="Auditorium-Seating-for-350" /> <label class="choice" id="labelinput" for="element_3_2">Auditorium Seating for 350 (recommended max for Performances</label> <label class="choice" for="element_3_3">Auditorium Seating 400</label> <input id="element_3_3" name="Room-Setup" class="elementcheckbox" type="checkbox" value="Auditorium-Seating-400" /> <label class="choice" for="element_3_4">Round Tables and Chairs</label> <input id="element_3_4" name="Room-Setup" class="elementcheckbox" type="checkbox" value="Round-Tables-and-Chairs" /> <label class="choice" for="element_3_5">Other (Please note that we cannot change the auditorium setup during your event*)</label> <input id="element_3_5" name="Room-Setup" class="elementcheckbox" type="checkbox" value="Other" /> </div> <div style="overflow:auto;"> <div style="float:right;"> <button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button> <button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button> </div> </div> <!-- Circles which indicates the steps of the form: --> <div style="text-align:center;margin-top:40px;"> <span class="step"></span> <span class="step"></span> <span class="step"></span> <span class="step"></span> </div> </form>
Put a second classname in <div class="tab theFirstTabWithCheckboxes"> This code get that element and counts cheched inputs to a global variable var checkboxes = document.querySelectorAll("tab.theFirstTabWithCheckboxes input"); var i = checkboxes.length; window.count_nr_of_checked_boxes = 0; while (i--) { //count down to 0 if (checkboxes[i].checked) window.count_nr_of_checked_boxes++; } With querySelectorAll() you select in the same way as you select in CSS. Note how the dot operator is an and operator and the space operator means "input is inside ..." (it is evaluated from right to left). Put the following line in the function validateForm() if (count_nr_of_checked_boxes < 1) valid = false You dont need to have window. on global variables
form enable by input radio id (checked)
I have two forms. I want one of them to be enabled by the user choose through one of the radio buttons above. What I want to do is to get the value of radio button and through this value make one of the forms enable. So I try with JavaScript and input radio ID but I had no success: <script> window.addEventListener('load', init); function init(){ var radio = document.getElementsByName ("questions"); for (var i=0; i<radios.length; i++) { if(radio[i].getElementById.checked == "questions_1") { radio[i].addEventListener(seleccionar); } } function trigger() { elementsForm = document.forms['question'].elements; for (var y=0; y<elementsForm.length; y++) { elementsForm[y].disabled=false; } } </script> Here's my code (HTML and CSS): .category_1 { height: 50px; padding-top: 2%; } .question { display: inline-flex; margin-left: 5%; } .q-text { font-weight: 600; } .q1 { width: 44%; } .q2 { width: 44%; } .form { display: inline-flex; margin-left: 5%; } .form.q1 { width: 44%; } .form.q2 { width: 44%; } .data { margin-top: 5%; } .data label { opacity: 0.5; } input[type=text] { width: 100%; } select { width: 100%; } textarea { width: 98%; } input[type=submit] { display: block; margin: auto; } <body> <div class="category_1"> <div class="question q1"><input type="radio" name="question" value="question_1" id="question_1"> <div class="q-text">QUESTION 1</div></div> <div class="question q2"><input type="radio" name="question" value="question_2" id="question_2"> <div class="q-text">QUESTION 2</div></div> </div> <div class="form q1"> <form name="q1" method="post" action=""> <div class ="data"> <label for="others">Name:</label> <input type="text" name="name" disabled> </div> <div class="data"> <label for="others">Select an item:</label> <select name="items-q1" disabled> <option value="it1">item 1</option> <option value="it2">item 2</option> <option value="it3">item 3</option> <option value="it4">item 4</option> </select> </div> <div class="data"> <label for="others">Anything else?</label> <textarea name="more" disabled></textarea> </div> <div class="data"> <input type="submit" value="submit" disabled> </div> </form> </div> <div class="form q2"> <form name="q2" method="post" action=""> <div class ="data"> <label for="others">Name:</label> <input type="text" name="name" disabled> </div> <div class ="data"> <label for="others">Choose an option:</label><br> <input type="radio" name="option" value="o1" disabled><label for="others">1</label> <input type="radio" name="option" value="o2" disabled><label for="others">2</label> </div> <div class="data"> <label for="others">Anything else?</label> <textarea name="more" disabled></textarea> </div> <div class="data"> <input type="submit" value="submit" disabled> </div> </form> </div> </body> I'd really appreciate any suggestions.
First of all i noticed your are getting the radios by the wrong name its "question" not "questions" <script> var rad = document.getElementsByName('question'); for(var i = 0; i < rad.length; i++) { rad[i].onclick = function() { if(this.value == "questions_1") { //input logic to display form //This is where you run isTrue() Function isTrue(data); } else{ //This is where you run isFalse() function isFalse(data); } }; } //Function to run if its true function isTrue(data){ //something equals something } //Function to run if its false function isFalse(data){ //something equals something } </script> Reference Link
<script> var rad = document.getElementsByName('question'); for(var i = 0; i < rad.length; i++) { rad[i].onclick = function () { if(this.value == "question_1") { e1(); }else { e2(); } }; } function e1() { var eform1 = document.querySelectorAll('.q1 input[type=text], .q1 select, .q1 textarea, .q1 input[type=submit]'); for(var i = 0; i < eform1.length; i++) { eform1[i].disabled = false; } } function e2() { var eform2 = document.querySelectorAll('.q2 input[type=text], .q2 input[type=radio], .q2 textarea, .q2 input[type=submit]'); for(var i = 0; i < eform2.length; i++) { eform2[i].disabled = false; } } </script>