COMP 126: Practical Web Design & Development for Everyone
<form method="POST" action="SOME_TARGET_URL">
...
</form>
<form> is a container for all the input-gathering elements inside it
the most common attributes of <form> are:
<form method="GET" action="https://www.google.com/search">
<input type="text" name="search-query">
</form>
Users need somewhere to enter the information they want to submit or request. We call these "fields". Each field should have its own unique "name" attribute and a type.
The most commonly used field types are
<form method="POST" action="/">
<label>
username
<input type="text" name="userid">
</label>
</form>
Labels tell users what information should go in each field. You can nest fields inside a label element to link the label with the field.
Or you can keep the label and input elements separate and link them with matching for and id attributes.
<form method="POST" action="/">
<label for="username">username</label>
<input id="username" type="text" name="userid">
</form>
<form method="POST" action="/">
<label>
username
<input type="text" name="userid">
</label>
<button type="submit">submit</button>
</form>
The <button> element is used to submit a form. Its type value is SUBMIT.
<input type="tel" name="phone">
<input type="email" name="email-address">
<input type="number" name="age">
<input type="url" name="website">
Used to group related fields; require a legend element
<fieldset>
<legend>Mother</legend>
<label for="mother-given">Given Name</label>
<input type="text" name="mother-given" id="mother-given">
<label for="mother-family">Family Name</label>
<input type="text" name="mother-family" id="mother-family">
</fieldset>
<fieldset>
<legend>Father</legend>
<label for="father-given">Given Name</label>
<input type="text" name="father-given" id="father-given">
<label for="father-family">Family Name</label>
<input type="text" name="father-family" id="father-family">
</fieldset>
Allow users to choose more than one option
<label>
<input type="checkbox" name="signup" value="yes">
Would you like to receive discounts by email?
</label>Selections from a group
<fieldset>
<legend>What languages do you speak?</legend>
<label>
<input type="checkbox" name="language" value="ar">
Arabic
</label>
<label>
<input type="checkbox" name="language" value="es">
Spanish
</label>
<label>
<input type="checkbox" name="language" value="en">
English
</label>
</fieldset>For a single selection from a group
<fieldset>
<legend>What is your preferred language?</legend>
<label>
<input type="radio" name="language" value="ar">
Arabic
</label>
<label>
<input type="radio" name="language" value="es">
Spanish
</label>
<label>
<input type="radio" name="language" value="en">
English
</label>
</fieldset>A no-server way to collect form data!
A standard HTML form...
...with a small amount of JavaScript (you don't have to write it) to capture the form data.
Instead of a complex back-end server, we'll use a Google Sheet as our database...
...resulting in a live contact form that drops submissions directly into a spreadsheet...
...a form that you will then style with CSS.
The front end (HTML/CSS/JS)
When the user fills out your form in their browser, JavaScript intercepts the "submit" click
The connection (the Internet)
The JavaScript sends the form data to a unique URL leading to a Google Sheet
The back end (Google Apps Script & Sheets)
A script living in a Google Sheet "catches" the data
The script adds the data as a new row in the spreadsheet
<div class="container">
<form id="contact-form">
<h3>get in touch!</h3>
<label for="name">Name:</label>
<input type="text" id="name" name="name" required />
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
<label for="message">Message:</label>
<textarea id="message" name="message" rows="4" required></textarea>
<button type="submit">Submit</button>
<p id="form-message"></p>
</form>
</div>Log into Google and create a new Google Sheet
Add column headers in the first row
These headers MUST exactly match the name attribute of your form inputs
You can choose your own headers (but they should not start with numbers or contain spaces!) depending on the needs of your form, as long as they're consistent with the associated name attributes of your form inputs
However, for this exercise, please use the headers timestamp, name, email, message
In your Google Sheet, go to Extensions > Apps Script; a new editor tab will open
Rename the project from "Untitled project" to something meaningful, like "Contact Form Handler"
Delete the default code inside the Code.gs file
This script will act like a mini-server. The doPost function will run whenever data is sent to its URL (which you'll get in a moment)
function doPost(e) {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var newRow = headers.map(function(header) {
return header.toLowerCase() === "timestamp" ? new Date() : e.parameter[header];
});
sheet.appendRow(newRow);
return ContentService
.createTextOutput(JSON.stringify({ "result": "success" }))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
return ContentService
.createTextOutput(JSON.stringify({ "result": "error", "error": error.message }))
.setMimeType(ContentService.MimeType.JSON);
}
}This step creates the public URL leading to the Google Sheet that your form will send data to.
Click the blue Deploy button and select New deployment
Click the gear icon and select Web app
Fill in the settings:
Description: Contact Form Processor
Execute as: your username
Who has access: Anyone <--- CRITICAL STEP!
Click Deploy
Google will prompt you to Authorize access
Choose your Google account. You may see a "Google hasn't verified this app" warning. Click Advanced, then Go to [your project name] (unsafe); click Allow
Success! A dialog box will appear with your Web App URL
COPY THIS URL. You will need it for your JavaScript
Take a look at the HTML form code you copied at the beginning of this exercise
Note a couple of things:
The <form> tag has an id--in this case, id="contact-form"
Each <input> and <textarea> has a name attribute that matches a header in your Google sheet
Place this <script> block just before your closing </body> tag:
<script>
const form = document.getElementById('contact-form');
const formMessage = document.getElementById('form-message');
const scriptURL = 'PASTE_YOUR_WEB_APP_URL_HERE';
form.addEventListener('submit', e => {
e.preventDefault();
formMessage.textContent = 'Sending...';
fetch(scriptURL, { method: 'POST', body: new FormData(form)})
.then(response => {
formMessage.textContent = 'Message sent successfully!';
form.reset();
setTimeout(() => { formMessage.textContent = ''; }, 5000);
})
.catch(error => {
formMessage.textContent = 'Oops! Something went wrong.';
console.error('Error!', error.message);
});
});
</script>As the value of
const scriptURL='PASTE_YOUR_WEB_APP_URL_HERE';
<script>
const form = document.getElementById('contact-form');
const formMessage = document.getElementById('form-message');
const scriptURL = 'PASTE_YOUR_WEB_APP_URL_HERE';
form.addEventListener('submit', e => {
e.preventDefault();
formMessage.textContent = 'Sending...';
fetch(scriptURL, { method: 'POST', body: new FormData(form)})
.then(response => {
formMessage.textContent = 'Message sent successfully!';
form.reset();
setTimeout(() => { formMessage.textContent = ''; }, 5000);
})
.catch(error => {
formMessage.textContent = 'Oops! Something went wrong.';
console.error('Error!', error.message);
});
});
</script>const form = ...
Selects our form from the DOM.
const scriptURL = '...'
This is where you paste the URL you copied earlier!
form.addEventListener('submit', ...)
Tells the browser to listen for the form's submit event.
e.preventDefault();
The most important line! It stops the browser from reloading the page, so our script can run instead.
fetch(scriptURL, ...)
Sends the collected form data (new FormData(form)) to our Google Script URL using the POST method.
.then(...) and .catch(...)
Handles the response from Google. We show a success or error message to the user.
Do your Google Sheet headers and name attributes match?
Do the column headers in your Google Sheet exactly match the name attributes in your HTML form? They are case sensitive (e.g., email vs Email)
Is the script deployed correctly?
Is the "Who has access" setting for your web app set to Anyone?
Is the URL correct?
Did you copy the full web app URL and paste it correctly into the scriptURL variable in your JavaScript?
Test! Fill out your form and watch the submission appear instantly in your Google Sheet
Customize! Add more fields to your form (and sheet!). Try adding phone number, subject, etc.
Style! Now on to the in-class exercise
required fields first, optional fields last
single-column layout is best for forms: better for usability and responsiveness
in most cases, and definitely on mobile labels should go ABOVE their fields to cut down on width
on desktop, labels can go to the left if it works
labels should be clear, concise, skimmable
We frequently select form elements for styling by element name or attribute. (You can still use classes and IDs if you prefer, though.)
Selecting the form element name
Form elements can be selected by referring to their element names:
input: selects all the input fieldstextarea: selects all the text areaslabel: selects all level elements
Selecting the form element attribute
Form elements can also be selected by referring to their attributes using element attribute selectors:
input[type=text]: selects all input fields having type attribute set to text
input[type=password]: selects all input fields having type attribute set to password
input[type=number]: selects all input fields having type attribute set to number
input[type=text] {
width: 100%;
padding: 12px;
margin: 10px 0;
box-sizing: border-box;
}
input[type=button], input[type=submit], input[type=reset] {
background-color: #04AA6D;
border: none;
color: white;
padding: 16px 32px;
text-decoration: none;
margin: 4px 2px;
cursor: pointer;
}
input,
textarea {
border: none;
padding: 0 10px;
margin: 0;
width: 80%;
background: none;
}