Over the last few articles in this series, we’ve learned how to use a handful of input types and validation attributes to natively validate forms.
We’ve learned how to use the Constraint Validation API to enhance the native browser validation process for a better overall user experience. And we wrote a polyfill to extend support all the way back to IE9 (and plug a few feature holes in some newer versions).
Now, let’s take what we’ve learned and apply it to a real example: the MailChimp signup form.
Article Series:
Constraint Validation in HTML
The Constraint Validation API in JavaScript
A Validity State API Polyfill
Validating the MailChimp Subscribe Form (You are here!)
A simple form with a large footprint
When you embed a MailChimp signup form on your site, it comes with a JavaScript validation script named `mc-validate.js`.
This file is 140kb (minified), and includes the entire jQuery library, two third-party plugins, and some custom MailChimp code. We can better!
See the Pen Form Validation: The MailChimp Standard Signup Form by Chris Ferdinandi (@cferdinandi) on CodePen.
Removing the bloat
First, let’s grab a MailChimp form without any of the bloat.
In MailChimp, where you get the code for your embeddable form, click on the tab labelled “Naked.” This version includes none of the MailChimp CSS or JavaScript.
data-recalc-dims=1

Subscribe to our mailing list

* indicates required


This is better, but it still includes some markup we don’t need. Let’s trim this down as much as possible.
We can remove the div#mc_embed_signup wrapper from around the form.
Similarly, we can remove the div#mc_embed_signup_scroll wrapper around the fields inside the form.
We can also remove the text informing visitors that “* indicates required.”
Let’s remove the .mc-field-group classes from around our form fields, and the empty class attributes on the fields themselves.
We should also remove the .required and .email classes from our email field, since they were only used as hooks for MailChimp validation script.
I went ahead and removed the * from the email label. It’s totally up to you how you want to label required fields, though.
We can delete the div#mce-responses container, which is only used by the MailChimp JavaScript file.
We can also remove the .clear class from the div around the submit button.
Let’s remove all of the empty value attributes.
Finally, we should remove the novalidate attribute from the form element. We’ll let our script add that for us when it loads.
All of this leaves us with a much more clean and modest looking form. Since the MailChimp CSS is removed, it will inherit your site’s default form styles.

Subscribe to our mailing list



See the Pen Form Validation: The MailChimp Simple Signup Form by Chris Ferdinandi (@cferdinandi) on CodePen.
Adding Constraint Validation
Now, let’s add in a few input types and validation attributes so that the browser can natively validate the form for us.
The type for the email field is already set to email, which is great. Let’s also add the required attribute, and a pattern to force emails to include a TLD (the .com part of an address). We should also include a title letting people know they have to have a TLD.

Subscribe to our mailing list



Enhancing with the Constraint Validation API
This is a great starting point, but we can enhance the user experience by adding the form validation script we wrote earlier in this series.
See the Pen Form Validation: MailChimp with the Constraint Validation API by Chris Ferdinandi (@cferdinandi) on CodePen.
Our validation script is just 6.7kb before minification, making it 20x smaller than the one MailChimp provides. If we want to ensure support back to IE9, though, we should include our Validity State polyfill and Eli Grey’s classList.js polyfill.
See the Pen Form Validation: MailChimp with the API Script and Polyfills by Chris Ferdinandi (@cferdinandi) on CodePen.
That brings our total file size up to 15.5kb unminified—still 9× smaller than the MailChimp validation script.
Submitting the form with Ajax
The `mc-validate.js` script provided by MailChimp doesn’t just validate the form. It also submits it with Ajax and displays a status message.
When you click submit on our modified form, it redirects the visitor to the MailChimp site. That’s a totally valid way to do things.
But, we can also recreate MailChimp’s Ajax form submission without jQuery for a better user experience.
The first thing we want to do is prevent the form from submitting via a page reload like it normally would. In our submit event listener, we’re calling event.preventDefault if there are errors. Instead, let’s call it no matter what.
// Check all fields on submit
document.addEventListener(submit, function (event) {
// Only run on forms flagged for validation
if (!event.target.classList.contains(validate)) return;
// Prevent form from submitting
event.preventDefault();

}, false);
See the Pen Form Validation: MailChimp and Prevent Default on Submit by Chris Ferdinandi (@cferdinandi) on CodePen.
Using JSONP
The mc-validate.js script uses JSONP to get around cross-domain security errors.
JSONP works by loading the returned data as a script element in the document, which then passes that data into a callback function that does all of the heavy lifting.
Setting up our Submit URL
First, let’s set up a function we can run when our form is ready to be submitted, and call it in our submit event listener.
// Submit the form
var submitMailChimpForm = function (form) {
// Code goes here…
};
// Check all fields on submit
document.addEventListener(submit, function (event) {

// Otherwise, let the form submit normally
// You could also bolt in an Ajax form submit process here
submitMailChimpForm(event.target);
}, false);
The first thing we need to do is get the URL from the form’s action attribute.
// Submit the form
var submitMailChimpForm = function (form) {
// Get the Submit URL
var url = form.getAttribute(action);
};
In the `mc-validate.js` script, the /post?u= in the URL is replaced with /post-json?u=. We can do that quite easily with the replace() method.
// Submit the form
var submitMailChimpForm = function (form) {
// Get the Submit URL
var url = form.getAttribute(action);
url = url.replace(/post?u=, /post-json?u=);
};
Serializing our form data
Next, we want to grab all of the form field data and create a query string of key/value pairs from it. For example, FNAME=Freddie%20Chimp&[email protected].
Let’s create another function to handle this for us.
// Serialize the form data into a query string
var serialize = function (form) {
// Code goes here…
};
Now, we want to loop through all of our form fields and create key/value pairs. I’ll be building off of the work done by Simon Steinberger for this.
First, we’ll create a serialized variable set as an empty string.
// Serialize the form data into a query string
// Forked and modified from https://stackoverflow.com/a/30153391/1293256
var serialize = function (form) {
// Setup our serialized data
var serialized = ;
};
Now let’s grab all of the fields in our form using form.elements and loop through them.
If the field doesn’t have a name, is a submit or button, is disabled, or a file or reset input, we’ll skip it.
If it’s not a checkbox or radio (a nice catchall for select, textarea, and the various input types) or it is and it’s checked, we’ll convert it to a key/value pair, add an & at the beginning, and append it to our serialized string. We’ll also make sure to encode the key and value for use in a URL.
Finally, we’ll return the serialized string.
// Serialize the form data into a query string
// Forked and modified from https://stackoverflow.com/a/30153391/1293256
var serialize = function (form) {
// Setup our serialized data
var serialized = ;
// Loop through each field in the form
for (i = 0; i < form.elements.length; i++) { var field = form.elements[i]; // Dont serialize fields without a name, submits, buttons, file and reset inputs, and disabled fields if (!field.name || field.disabled || field.type === file || field.type === reset || field.type === submit || field.type === button) continue; // Convert field data to a query string if ((field.type !== checkbox && field.type !== radio) || field.checked) { serialized += & + encodeURIComponent(field.name) + = + encodeURIComponent(field.value); } } return serialized; }; See the Pen Form Validation: MailChimp with Ajax Submit – Serialized Form Data by Chris Ferdinandi (@cferdinandi) on CodePen. Now that we have our serialized form data, we can add it to our URL. // Submit the form var submitMailChimpForm = function (form) { // Get the Submit URL var url = form.getAttribute(action); url = url.replace(/post?u=, /post-json?u=); url += serialize(form); }; Adding a callback A key part of how JSONP works is the callback. Traditional Ajax requests return data back to you. JSONP instead passes data into a callback function. This function has to be global (as in, attached to the window rather than inside of another function). Let’s create a callback function, and log the returned data in the console so that we can see what MailChimp sends back. // Display the form status var displayMailChimpStatus = function (data) { console.log(data); }; Now we can add this callback to our URL. Most JSONP use callback as the query string key for this, but MailChimp uses c. // Submit the form var submitMailChimpForm = function (form) { // Get the Submit URL var url = form.getAttribute(action); url = url.replace(/post?u=, /post-json?u=); url += serialize(form) + &c=displayMailChimpStatus; }; Injecting our script into the DOM Now we’re ready to inject our script into the DOM. First, we’ll create a new script element and assign our URL as it’s src. // Submit the form var submitMailChimpForm = function (form) { // Get the Submit URL var url = form.getAttribute(action); url = url.replace(/post?u=, /post-json?u=); url += serialize(form) + &c=displayMailChimpStatus; // Create script with url and callback (if specified) var script = window.document.createElement( script ); script.src = url; }; Next, we’ll grab the first