Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save badasscodr/23a0dcba897509824b11db7001241591 to your computer and use it in GitHub Desktop.
Save badasscodr/23a0dcba897509824b11db7001241591 to your computer and use it in GitHub Desktop.
How to Get Form Field Data as JSON Using Plain JavaScript

How to Get Form Field Data as JSON Using Plain JavaScript

A plain JavaScript example of extracting a form's fields into an object using the field name as the key and the field value as the value. This can work in lieu of things like jQuery's .serialize() or .serializeArray(), which leave something to be desired if we need to work with the form data in JavaScript.

A Pen by Jason Lengstorf on CodePen.

License.

<section class="content">
<h1 class="content__heading">Send Me a Message</h1>
<p class="content__lede">Use this handy contact form to get in touch with me.</p>
<form class="content__form contact-form">
<div class="testing">
<p>Does this do anything?</p>
</div>
<div class="contact-form__input-group">
<input class="contact-form__input contact-form__input--radio" id="salutation-mr" name="salutation" type="radio" value="Mr."/>
<label class="contact-form__label contact-form__label--radio" for="salutation-mr">Mr.</label>
<input class="contact-form__input contact-form__input--radio" id="salutation-mrs" name="salutation" type="radio" value="Mrs."/>
<label class="contact-form__label contact-form__label--radio" for="salutation-mrs">Mrs.</label>
<input class="contact-form__input contact-form__input--radio" id="salutation-ms" name="salutation" type="radio" value="Ms."/>
<label class="contact-form__label contact-form__label--radio" for="salutation-ms">Ms.</label>
</div>
<div class="contact-form__input-group">
<label class="contact-form__label" for="name">Full Name</label>
<input class="contact-form__input contact-form__input--text" id="name" name="name" type="text"/>
</div>
<div class="contact-form__input-group">
<label class="contact-form__label" for="email">Email Address</label>
<input class="contact-form__input contact-form__input--email" id="email" name="email" type="email"/>
</div>
<div class="contact-form__input-group">
<label class="contact-form__label" for="subject">How can I help you?</label>
<select class="contact-form__input contact-form__input--select" id="subject" name="subject">
<option>I have a problem.</option>
<option>I have a general question.</option>
</select>
</div>
<div class="contact-form__input-group">
<label class="contact-form__label" for="message">Enter a Message</label>
<textarea class="contact-form__input contact-form__input--textarea" id="message" name="message" rows="6" cols="65"></textarea>
</div>
<div class="contact-form__input-group">
<p class="contact-form__label--checkbox-group">Please send me:</p>
<input class="contact-form__input contact-form__input--checkbox" id="snacks-pizza" name="snacks" type="checkbox" value="pizza"/>
<label class="contact-form__label contact-form__label--checkbox" for="snacks-pizza">Pizza</label>
<input class="contact-form__input contact-form__input--checkbox" id="snacks-cake" name="snacks" type="checkbox" value="cake"/>
<label class="contact-form__label contact-form__label--checkbox" for="snacks-cake">Cake</label>
</div>
<input name="secret" type="hidden" value="1b3a9374-1a8e-434e-90ab-21aa7b9b80e7"/>
<button class="contact-form__button" type="submit">Send It!</button>
</form>
</section>
<div class="results">
<h2 class="results__heading">Form Data</h2>
<pre class="results__display-wrapper"><code class="results__display"></code></pre>
</div>
/**
* Checks that an element has a non-empty `name` and `value` property.
* @param {Element} element the element to check
* @return {Bool} true if the element is an input, false if not
*/
const isValidElement = element => {
return element.name && element.value;
};
/**
* Checks if an element’s value can be saved (e.g. not an unselected checkbox).
* @param {Element} element the element to check
* @return {Boolean} true if the value should be added, false if not
*/
const isValidValue = element => {
return (!['checkbox', 'radio'].includes(element.type) || element.checked);
};
/**
* Checks if an input is a checkbox, because checkboxes allow multiple values.
* @param {Element} element the element to check
* @return {Boolean} true if the element is a checkbox, false if not
*/
const isCheckbox = element => element.type === 'checkbox';
/**
* Checks if an input is a `select` with the `multiple` attribute.
* @param {Element} element the element to check
* @return {Boolean} true if the element is a multiselect, false if not
*/
const isMultiSelect = element => element.options && element.multiple;
/**
* Retrieves the selected options from a multi-select as an array.
* @param {HTMLOptionsCollection} options the options for the select
* @return {Array} an array of selected option values
*/
const getSelectValues = options => [].reduce.call(options, (values, option) => {
return option.selected ? values.concat(option.value) : values;
}, []);
/**
* A more verbose implementation of `formToJSON()` to explain how it works.
*
* NOTE: This function is unused, and is only here for the purpose of explaining how
* reducing form elements works.
*
* @param {HTMLFormControlsCollection} elements the form elements
* @return {Object} form data as an object literal
*/
const formToJSON_deconstructed = elements => {
// This is the function that is called on each element of the array.
const reducerFunction = (data, element) => {
// Add the current field to the object.
data[element.name] = element.value;
// For the demo only: show each step in the reducer’s progress.
console.log(JSON.stringify(data));
return data;
};
// This is used as the initial value of `data` in `reducerFunction()`.
const reducerInitialValue = {};
// To help visualize what happens, log the inital value, which we know is `{}`.
console.log('Initial `data` value:', JSON.stringify(reducerInitialValue));
// Now we reduce by `call`-ing `Array.prototype.reduce()` on `elements`.
const formData = [].reduce.call(elements, reducerFunction, reducerInitialValue);
// The result is then returned for use elsewhere.
return formData;
};
/**
* Retrieves input data from a form and returns it as a JSON object.
* @param {HTMLFormControlsCollection} elements the form elements
* @return {Object} form data as an object literal
*/
const formToJSON = elements => [].reduce.call(elements, (data, element) => {
// Make sure the element has the required properties and should be added.
if (isValidElement(element) && isValidValue(element)) {
/*
* Some fields allow for more than one value, so we need to check if this
* is one of those fields and, if so, store the values as an array.
*/
if (isCheckbox(element)) {
data[element.name] = (data[element.name] || []).concat(element.value);
} else if (isMultiSelect(element)) {
data[element.name] = getSelectValues(element);
} else {
data[element.name] = element.value;
}
}
return data;
}, {});
/**
* A handler function to prevent default submission and run our custom script.
* @param {Event} event the submit event triggered by the user
* @return {void}
*/
const handleFormSubmit = event => {
// Stop the form from submitting since we’re handling that with AJAX.
event.preventDefault();
// Call our function to get the form data.
const data = formToJSON(form.elements);
// Demo only: print the form data onscreen as a formatted JSON object.
const dataContainer = document.getElementsByClassName('results__display')[0];
// Use `JSON.stringify()` to make the output valid, human-readable JSON.
dataContainer.textContent = JSON.stringify(data, null, " ");
// ...this is where we’d actually do something with the form data...
};
/*
* This is where things actually get started. We find the form element using
* its class name, then attach the `handleFormSubmit()` function to the
* `submit` event.
*/
const form = document.getElementsByClassName('contact-form')[0];
form.addEventListener('submit', handleFormSubmit);
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.13.0/polyfill.min.js"></script>
@use postcss-nested;
@use postcss-simple-vars;
@use postcss-cssnext;
$color-lightest: #f9fdfe;
$color-gray-light: #cdcfcf;
$color-gray-medium: #686a69;
$color-gray-dark: #414643;
$color-darkest: #2a2f2c;
/* A simple reset. */
*,::before,::after {
margin: 0;
box-sizing: border-box;
}
/* Heydon Pickering’s lobotomized owl. Details: https://bit.ly/1H7MXUD */
*+* {
margin-top: 1rem;
}
/* Set up fonts, colors and all that jazz. */
body {
background: $color-lightest;
color: $color-gray-medium;
font-family: 'Open Sans', sans-serif;
font-size: 18px;
line-height: 1.75;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Headings use a different font because they’re hipsters. */
h1,h2 {
color: $color-darkest;
font-family: Lato, sans-serif;
font-weight: 300;
line-height: 1.125;
}
/* Set up general layout rules for outer containers. */
.content,.results {
width: 90vw;
max-width: 550px;
margin: 8vh auto;
}
.content {
&__heading {
font-size: 125%;
}
&__lede {
margin-top: 0.5rem;
font-size: 87.5%;
}
}
.results {
&__heading {
font-size: 110%;
}
&__display-wrapper {
margin-top: 1rem;
padding: 0.5rem 1rem;
background: $color-lightest;
border: 1px solid $color-gray-light;
overflow-x: scroll;
}
}
.contact-form {
position: relative;
display: block;
margin: 0;
padding: 1rem 0 2rem;
border-top: 1px solid $color-gray-light;
border-bottom: 1px solid $color-gray-light;
overflow: hidden;
&__input-group {
margin-top: 0.25rem;
padding: 0.5rem 1rem;
}
&__label {
display: block;
color: $color-gray-dark;
font-family: Lato, sans-serif;
font-size: 75%;
line-height: 1.125;
&--checkbox-group {
display: inline-block;
margin-right: 1rem;
font-size: 75%;
}
&--checkbox,&--radio {
display: inline-block;
margin-left: 0.25rem;
}
}
&__input {
display: block;
margin-top: 0;
padding: 0.5rem 0.75rem;
border: 1px solid $color-gray-light;
width: 100%;
font-family: 'Open Sans', sans-serif;
font-size: 1rem;
transition: 150ms border-color linear;
&--checkbox,&--radio {
display: inline-block;
width: auto;
&~& {
margin-left: 1rem;
}
}
&:focus,&:active {
border-color: $color-gray-medium;
outline: 0;
}
}
&__button {
display: block;
margin: 0.5rem 1rem 0;
padding: 0 1rem 0.125rem;
background-color: $color-gray-medium;
border: 0;
color: $color-lightest;
font-family: lato, sans-serif;
font-size: 100%;
letter-spacing: 0.05em;
line-height: 1.5;
text-transform: uppercase;
transition: 150ms all linear;
&:hover,&:active,&:focus {
background: $color-darkest;
cursor: pointer;
outline: 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment