DZone
Web Dev Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Web Dev Zone > Saving Form Data in Client-side Storage

Saving Form Data in Client-side Storage

A look at using vanilla JavaScript to automatically store and cache form data as a user enters information.

Raymond Camden user avatar by
Raymond Camden
·
Apr. 13, 22 · Web Dev Zone · Analysis
Like (3)
Save
Tweet
2.23K Views

Join the DZone community and get the full member experience.

Join For Free

Today's post is one of those that started off with me worrying that it was going to be too simple and quickly turned into a bit of a complex little beast. I love that as it usually means my expectations were wrong and I've got a chance to expand my knowledge a bit. This post came from a simple idea: While working on a form, can we save your form data for restoring later in case you navigate away, close the tab by accident, or perhaps get "surprised" by an operating system update. While this is not something you would want to use in every situation (for example, storing a new password field), there are plenty of examples where this could be helpful, especially in a larger form.

For our demo, I will only cover client-side storage, which means the data will be unique to one browser on the device. Although what I described here could be tied to a back-end service for storing temporary form data as well.

Initially, I tried to build a generic solution that would apply to any and all forms but quickly discovered that it became Non-Trivial to the point where I decided a more hard-coded solution would be better. My assumption here is that my readers can take these techniques and apply them to their site with a bit of work. As always, if you have any questions, just let me know!

The Form

Alright, let's start off by looking at the form I'll use for the demo. While covering every unique kind of form field would be overwhelming, I tried to cover the main ones: A few text fields, a set of checkboxes, a set of radio fields, and a text area. Here's the HTML:

HTML
 
<form id="mainForm">
	<p>
	<label for="name">Name</label>
	<input type="text" name="name" id="name">
	</p>
	<p>
	<label for="email">Email</label>
	<input type="email" name="email" id="email">
	</p>
	<p>
	<label for="inus">In US?</label>
	<select name="inus" id="inus">
		<option></option>
		<option value="true">Yes</option>
		<option value="false">No</option>
	</select>
	</p>
	<p>
		<label for="department">Department</label><br/>
		<input type="radio" name="department" id="dept1" value="dept1"><label for="dept1">Dept 1</label><br/>
		<input type="radio" name="department" id="dept2" value="dept2"><label for="dept2">Dept 2</label><br/>
		<input type="radio" name="department" id="dept3" value="dept3"><label for="dept3">Dept 3</label><br/>
</p>
<p>
	<label for="cookie">Favorite Cookie (Select as many as you want):</label><br/>
	<input type="checkbox" name="cookie" id="cookie1" value="Chocolate Chip"><label for="cookie1">Chocolate Chip</label><br/>
	<input type="checkbox" name="cookie" id="cookie2" value="Sugar"><label for="cookie2">Sugar</label><br/>
	<input type="checkbox" name="cookie" id="cookie3" value="Ginger"><label for="cookie3">Ginger</label><br/>
	<input type="checkbox" name="cookie" id="cookie4" value="BW"><label for="cookie4">Black & White</label><br/>
</p>		
<p>
	<label for="comments">Comments</label><br/>
	<textarea name="comments" id="comments"></textarea>
</p>
<p>
	<input type="submit">
</p>
</form>

It's not terribly exciting but gets the job done in terms of demonstrating multiple types of form fields. 

html form

Saving Form Data

Let's begin with how to save the form. Here's the high-level approach I'm going to use:

  • The data will be stored in LocalStorage. This will let it persist forever (not really, but close enough) and will be an incredibly simple API to work with. IndexedDB can store a lot more data, but all we're storing is a form.
  • I will persist the data on every change. We could get fancy and save on an interval, but it's relatively inexpensive to just save on every change.

To begin, I set up my code to fire some logic on DOMContentLoaded as well as create some global variables:

JavaScript
 
document.addEventListener('DOMContentLoaded',init,false);

let name, email, inus, depts, cookies, comments;

Now let's look at init: 

JavaScript
 
function init() {
	// get the dom objects one time
	name = document.querySelector('#name');
	email = document.querySelector('#email');
	inus = document.querySelector('#inus');
	depts = document.querySelectorAll('input[name=department]');
	cookies = document.querySelectorAll('input[name=cookie]');
	comments = document.querySelector('#comments');
	
	// listen for input on all
	let elems = Array.from(document.querySelectorAll('#mainForm input, #mainForm select, #mainForm textarea'));
	elems.forEach(e => e.addEventListener('input', handleChange, false));
}

As I mentioned above, I'm not going for a generic solution, but rather one tied to my exact form. You can see then I create a variable representing the DOM item for each of my fields. depts and cookies ae special as they are a set of items, not just one.

But while I'm not going dynamic to set up the variables pointing to the form fields, I did go dynamic to set up the event handler. I could have added an event listener for each of my variables (while ensuring I handled depts and cookies in a loop), but this shortcut handles matching any form fields inside my form and then letting me quickly assign the handler for each.

Now that we've got event handlers, we can build logic to persist the form. This handler will fire on any change in the fields, but as I said above, we'll get all the data and persist.

JavaScript
 
function handleChange(e) {
	
	console.log('handleChange');
	/*
	get all values and store
	first the easy ones
	*/
	let form = {};
	form.name = name.value;
	form.email = email.value;
	form.inus = inus.value;
	form.comments = comments.value;
	// either null or one
	depts.forEach(d => {
		if(d.checked) form.department = d.value;
	});
	// either empty array or some things
	form.cookies = [];
	cookies.forEach(c => {
		if(c.checked) form.cookies.push(c.value);
	});
	
	// now store
	saveForm(form);
}

I create an object, formto store my data, and then get the "simple" ones where I can just check the value. This works for the select tag too. For the radio and checkbox ones, I handle them a bit differently. The radio one, deptswill either have nothing selected or one, so if nothing is picked, it's never saved, or it's a value. For cookies, I'll always have an empty array at a minimum, but will fill it with the values when selected.

Finally, I take the data and pass it to another function. The code to use LocalStorage is very simple, but I wanted it abstracted in case the decision was made to change to something else in the future. Here's that function:

JavaScript
 
function saveForm(form) {
	let f = JSON.stringify(form);
	window.localStorage.setItem('form', f);
}

Remember that LocalStorage only takes simple values, so the object is serialized to a string first.

Woot! Ok, at this point, I can type in data, and confirm it's working in DevTools:
Devtools output

Retrieving the Data

Now that there's a way to store the form, let's look at fetching the data (if it exists) and using it. Back in init, I added the following:

JavaScript
 
// do we have a cached form?
let cached = getForm();
if(cached) {
	name.value = cached.name;
	email.value = cached.email;
	inus.value = cached.inus;
	comments.value = cached.comments;
	if(cached.department) {
		depts.forEach(d => {
			if(d.value === cached.department) d.checked = true;
		});
	}
	if(cached.cookies) {
		cookies.forEach(c => {
			if(cached.cookies.includes(c.value)) c.checked = true;
		});
	}
}

I begin by fetching the form (I'll show that in a second) and if the cache exists, I make use of it. All the simple values and the select, it's easy to set. For the checkbox and radio ones, it's slightly more complex. depts will either be null or a value, but cookies will be an array (technically it will always exist so if isn't really necessary) and I make use includes to check the cached array.

As with saveForm, I wanted to wrap the cache retrieval logic to handle updating the storage in the future. Here's getForm:

JavaScript
 
function getForm() {
	let f = window.localStorage.getItem('form');
	if(f) return JSON.parse(f);
}

There's one last thing to do. When the form is submitted, it makes sense to clear the cache. I added this to the end of init: 

JavaScript
 
document.querySelector('#mainForm').addEventListener('submit', () => {
	window.localStorage.removeItem('form');
}, false);

This is nice and simple, but I'm being a bit inconsistent here by not abstracting out how I work with persistence. It's one line, and I feel kinda bad about it, but I'm also fine leaving it for now.

Here's the complete demo for you to play with: Demo

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 8 Must-Have Project Reports You Can Use Today
  • Major PostgreSQL Features You Should Know About
  • Monolith vs Microservices Architecture: To Split or Not to Split?
  • The Power of Enum: Make Your Code More Readable and Efficient [Video]

Comments

Web Dev Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo