News -

Giving superpowers to Contact Form 7.

The situation is this: you have forms on your WordPress site that have been built using the ever-present Contact Form 7 (CF7) plugin. It was a very easy implementation at the time, it checks all of the requirements that the client put forward and in general, has been working well because it:

  • Offers server-side validation, as well as hooks for creating your own validations;
  • Provides an easy mark-up for creating forms in the WP backend that really anyone (well maybe not anyone) could use;
  • Sends up to two confirmation emails, supporting HTML for those of us that love to code for decade-old rendering engines (I’m looking at you Outlook); and
  • Allows for embedding these magical forms all over your site with the use of shortcodes.

You may have used an additional plugin here and there to add functionality, but natively, CF7 was the cheat code to get forms up and running fast.

A bit of time has passed, and the client has come back to you with some requests for new functionality. These could be:

“I want the form to be dynamic, I only want users to fill in this part of the form if they select Option A, but this part of the form if they select Option B”.

“Our CRM can only handle specific formats for phone number codes, so any time you capture this detail, it needs to be in this format.”

“We have to add some fields and now our form has 100 fields to fill in, it seems a bit rude to try and make a user do that all in one go.”

Right now you’re feeling overwhelmed by the two very unappealing options you face:

  1. Start over. Build the form from scratch. Add the new functionality after writing your own solutions to send confirmation emails, field validation, and general form behaviour.
  2. Use a cocktail of plugins to reach a solution, and hope it turns out more like the long-island-ice-tea kind rather than the Molotov kind.

Avoidance behaviour begins to manifest because both of these options seem soul-suckingly kludgy. (what a word for such awkward meaning!)

Right, so now I’ve thrust you into this conundrum, it would only be fitting of me to offer you a solution. The compromise will allow you to create new, reusable functionality and maintain the status quo. Am I a hero for saving you or a villain for putting you in this predicament in the first place? You decide.

— JQUERY VALIDATE —

It’s the shining lights, humming-angels reveal that means it MUST be the only way to proceed. This plugin will allow you to validate fields client-side, create your own validation rules, validate programmatically, and re-use the functionality across forms. It’s your one-stop-shop, for all things forms and now it’s time to show you the how and why. Here’s some functionality that you can build out using CF7 and jQuery Validate.

Multistep forms

The main mistake I’ve made in the past in attempting to create multistep forms is allowing a user to proceed while there are invalid entries in the current step. This leaves the user scratching their head when their final submit doesn’t work, they have an error message begging them to check the highlighted fields, and the highlighted field isn’t visible.

Since base CF7 doesn’t have a ‘validate’ button or a ‘next step’ button, jQuery Validate swoops in and saves the day by allowing us to programmatically validate client-side. Bind an event listener to the ‘next’ button at the end of your form’s step to check that the current page is all valid, then set all your visibility and accessibility attributes to transition to the next step.

form.find('[next-step-button]').on('click', function (e) {
    if (currentStep < nextStep && form.valid()) {
      gotoStep(nextStep);
    } else if ( nextStep < currentStep) {
      gotoStep(nextStep);
    }
  });

Invalid form? You shall not pass!

Remember to allow users to navigate back to previous steps without needing valid entries on the current page.

Dynamic fields

If the flow of filling out your form is more like a goosebumps adventure than a novel, and there are many if-this-then-that routes to take, your plain CF7 can handle a bit of conditional logic to display fields to guide the user. The issue we run into is that the server-side validation of CF7 will mean that regardless of whether a field is visible or not, any validations will still be applied on submit. If there are a few hidden fields marked as required, successfully filling in your form has turned into an Easter egg hunt.

jQuery Validate’s (best?) feature is that it does not attempt to validate hidden fields. If your field has display:none or visibility:hidden, it will be skipped when validating the form. This is incredibly useful when validating forms that have fields that are conditionally displayed.

Another key consideration when implementing dynamic fields is to decide what happens when users go backward. If answering ‘Yes, absolutely’ to ‘Would you like to provide feedback?’ triggers a series of questions relating to a user’s experience, but answer ‘No, I’d rather not’ progresses you to a completely different line of questions, what happens if a user continues along a path, only to change the answer to an earlier question that sets them up for a completely different journey?

Well, the unfortunately vague answer is: it depends. Is there harm in keeping the captured data? Will this change the behaviour of the form later on? Will clearing the subsequent fields enrage the user if they change their mind back and find all of their precious form data gone? The complexity of your situation and answers to these questions will dictate your approach.

So let’s dive into the nitty-gritty of how to do this. CF7 tags don’t currently support adding random attributes in the field shortcodes. E.g [text id:my-field class:my-class data-js:my-random-value placeholder “This is a text field”] is not going to work, meaning that it can be hard to structure data that will change a form’s behaviour.

The solution here is to simply add data attributes that can dictate dynamic behaviour in a parent element. I’ve used a JSON array that matches the field value with the new fields it needs to trigger, meaning that creating new dynamic behaviour is all done in the CF7 interface with HTML rather than needing to alter any JavaScript.

<div class="row">
  <div class="col-xs-24">
    <p class="form__row" data-js-trigger='[{"Other": "salutation-other-text"}]'>
      <label for="sal">Salutation</label>
      [select sal id="sal" use_label_element include_blank "Option 1"]
    </p>
  </div>
</div>

Nothing like the smell of Bootstrap in the morning

Next, you can use event listeners on inputs to take the value of the field and change the visibility of any subsequent elements on the page. Something like this should do:

$('#do-you-want-to-subscribe-checkbox').on('change', function (e) {
    if( e.currentTarget.value) {
      $('#email-input').toggleClass(
          'is-visible',
          e.currentTarget.checked
      );
    }
  });

Shut up and take my email!

Flexibility

Your ability to control form behaviour will grow with the use of jQuery Validate. In creating the Validator object that will expose the methods you’ll use to validate the form, there’s a multitude of options that will allow you to modify the default behaviour of the plugin.

For starters, there’s a number of places to insert custom callback functions, including submit handlers, error handlers. You can modify how error messages are handled, what happens to focus on error, and even set validations messages according to what kind of validation error is triggered.

Remember that there are also events triggered by CF7 on submission and success. Between these two sets, you should be able to create some engaging user experiences.

Another powerful and useful feature is the ability to create custom validations. Do you only want to accept emails with a certain domain? Maybe your application is a little more bespoke and you require numbers in a very particular format, according to number of days until the autumnal equinox. Well, if you can code it, you can validate it. Here’s an example of a simple date validation:

jQuery.validator.addMethod(
    'onlyDatesBeforeToday',
    function (value, element) {
      if ( value === '') return true;
      var currentDate = new Date();
      var inputDate = new Date(value);
      if (inputDate < currentDate) return true;
      return false;
    },
    'Only enter dates before today'
  );

The 11th commandment: Thou shalt not be born in the future.

Finally, don’t mix and match validations. Once you’ve made the decision to move away from CF7 for validation, don’t get attached to anything. This means telephone numbers, email addresses, required fields… everything. All of this can be replicated with jQuery Validate and implemented in your form by adding some classes to these fields.

[text donuts-flavours id:donut-flavours class:jquery-required placeholder "We simply MUST know"]

I’m so glad donut ask, donut tell isn’t a thing anymore.

So, if you’ve built out a bunch of forms using CF7 and now have a need to add dynamic fields, break up the form into multiple steps, or create some funky validations, jQuery Validate may be the solution. It will allow you greater depths of control when it comes to form behaviour and can act as a simple bolt-on, creating a library of functionality for you to use in the future. The number of supported options will provide gratuitous amounts of freedom and allow you to build out the form of your dreams, getting runs on the board and ultimately keeping clients happy.