Freeform Javascript Plugin
If you wish to extend the capabilities of Freeform's front-end Javascript plugin, please refer to the documentation below:
You're viewing the v3.9 version of these docs. To switch to the v3.10 docs, click here to view the revised v3.10 documentation that shows usage of the improved Freeform Javascript plugin.
Getting the Plugin Instance
Let's assume you have a form tag with an ID in your HTML like so:
<form id="my-form">...</form>
You can get the plugin instance in two ways, whichever is more convenient to you:
Once you have the form element, you can either use the static ::getInstance()
method on the Freeform
plugin class, like so:
const form = document.getElementById('my-form');
const freeform = Freeform.getInstance(form);
Or you can access the Freeform JS Plugin instance via the element's freeform
property, like so:
const form = document.getElementById('my-form');
const freeform = form.freeform;
To hook into the Freeform JS plugin once the plugin has been initialised your form element has to be listening to the freeform-ready
event:
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
// your custom code here
});
When to call your Custom Scripts
When setting form scripts to be loaded inside forms, the Freeform JS plugin instance will be accessible to scripts that are called inside the footer. Load your custom script in the footer and call the Freeform JS plugin instance directly from the form object, like so:
const form = document.getElementById('my-form');
form.freeform.setOption(
'successBannerMessage',
'This is a custom success message'
);
If you have set the form scripts to be loaded inside the footer, then it's best to attach an event listener to the form object which will execute once the Freeform JS plugin instance has finished initializing. Use this script before the rest of the footer scripts, but after the form has been rendered, like so:
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
event.target.freeform.setOption(
'successBannerMessage',
'This is a custom success message'
);
});
Loading Freeform JS manually
If you wish to manually load the Freeform JS elsewhere in your template (rather than having Freeform automatically insert it inside the Freeform form or in the page footer), be sure to select the None
option for the Freeform Javascript Insertion Location setting. Then, you'll need to add the freeform.loadFreeformScripts()
function to your template where you'd like it to insert the JS and pass it the form handle.
Here's a simple example of how this may look in your template:
{% set form = craft.freeform.form('myForm') %}
{{ form.render }}
{{ craft.freeform.loadFreeformScripts(form) }}
If you happen to be caching your form, the randomly generated form anchor would be incorrect. To get around this, you'll need to also insert a small script that replaces the form anchor with the current one (similar to how you would have to replace the CSRF token, etc). You would need to place the freeform.loadFreeformScripts()
function anywhere after the cache
tag closes.
{% set form = craft.freeform.form('myForm') %}
{% cache %}
<div class="container">
{{ form.render }}
</div>
{% endcache %}
<script>
// Replace all form anchors with the current one. If you have more than one Freeform form on the page, you would have to use ID's to find the form you wish to run this replacement on.
document.querySelector('form[data-id]').dataset.id = "{{ form.anchor }}";
</script>
{{ craft.freeform.loadFreeformScripts(form) }}
Adding callbacks
Once you have the plugin instance, you can attach callbacks to it to perform the things you intend to perform:
On Submit callback
To perform something when the form is being submitted and decide whether it should finish submitting or stop, use ::addOnSubmitCallback(callback)
method. It lets you add a callback function to the stack of callback functions which will run before the form is submitted. Returning false
in the callback, will prevent the form from being submitted. The callback function receives two arguments - the form element and the freeform plugin's options.
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
freeform.addOnSubmitCallback((formElement, options, isBackButtonPressed) => {
if (isBackButtonPressed) {
// Skip any processes if the back button is pressed
return true;
}
return formElement.dataset.myDataItem !== 'not-what-im-looking-for';
});
});
AJAX callbacks
If the form has AJAX enabled, a couple more callbacks can be added to the form.
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
freeform.addOnSuccessfulAjaxSubmit((event, form, response) => {
// Do something on a successful ajax submit
});
});
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
freeform.addOnFailedAjaxSubmit((event, form, response) => {
// Do something on a failed ajax submit
});
});
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
freeform.addOnAfterAjaxSubmit((event, form, response) => {
// Do something after every ajax submit is completed, regardless of it being successful or not
});
});
Plugin Options
The Freeform JS Plugin has several options that let you override the way it works. Mainly, the options are for rendering AJAX form success or failure messages.
You can override the options by using the ::setOption(name, value)
method:
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
// Enable AJAX. This happens automatically if you enable AJAX via the form builder
freeform.setOption('ajax', true);
// Attach a custom function that handles message removal, if you need custom logic.
freeform.setOption('removeMessages', function () {
// This is bound to the Freeform instance
this.form.querySelectorAll('.ff-errors').remove();
this.form.querySelectorAll('.ff-form-success').remove();
});
});
The available options are:
successBannerMessage
- a string containing the success message you want to see if the AJAX form has been successfully posted.errorBannerMessage
- a string containing the error message you want to see if the AJAX form has failed.errorClassBanner
- a string containing the class name of the banner that contains form error messages.errorClassList
- a string containing the class name of the error message list element under invalid fields.errorClassField
- a string containing the class name of input elements that contain errors.successClassBanner
- a string containing the class name of the banner that contains the success message.removeMessages
- a function that handles removing all error and success messages from a form.renderSuccess
- a function that creates and displays the success banner.renderFormErrors
- a function that creates and displays the form error message banner. Receivesmessages
as the first argument.renderFieldErrors
- a function that creates and displays the field error messages. Receivesmessages
as the first argument.
Examples:
const form = document.getElementById('my-form');
form.addEventListener('freeform-ready', function (event) {
const freeform = event.target.freeform;
freeform.setOption('successBannerMessage', 'My custom success message');
freeform.setOption('errorBannerMessage', 'My custom error message');
freeform.setOption('errorClassBanner', 'my-custom-error-banner');
freeform.setOption('errorClassList', 'my-custom-errors-list');
freeform.setOption('errorClassField', 'this-field-has-errors');
freeform.setOption('successClassBanner', 'my-custom-success-banner');
freeform.setOption('removeMessages', function () {
this.form.querySelectorAll('.my-custom-error-banner').remove();
this.form.querySelectorAll('.my-custom-success-banner').remove();
this.form.querySelectorAll('.my-custom-errors-list').remove();
});
freeform.setOption('renderSuccess', function () {
const successMessage = document.createElement('div');
successMessage.classList.add('my-custom-success-banner');
successMessage.appendChild(
document.createTextNode('Form submitted successfully')
);
this.form.insertBefore(successMessage, this.form.childNodes[0]);
});
freeform.setOption('renderFormErrors', function (errors) {
const errorBlock = document.createElement('div');
errorBlock.classList.add('my-custom-errors-banner');
const paragraph = document.createElement('p');
paragraph.appendChild(document.createTextNode('Form contains errors!'));
errorBlock.appendChild(paragraph);
if (errors.length) {
const errorsList = document.createElement('ul');
for (let messageIndex = 0; messageIndex < errors.length; messageIndex++) {
const message = errors[messageIndex];
const listItem = document.createElement('li');
listItem.appendChild(document.createTextNode(message));
errorsList.appendChild(listItem);
}
errorBlock.appendChild(errorsList);
}
this.form.insertBefore(errorBlock, this.form.childNodes[0]);
});
freeform.setOption('renderFieldErrors', function (errors) {
for (const key in errors) {
if (!errors.hasOwnProperty(key) || !key) {
continue;
}
const messages = errors[key];
const errorsList = document.createElement('ul');
errorsList.classList.add('my-custom-errors-list');
for (
let messageIndex = 0;
messageIndex < messages.length;
messageIndex++
) {
const message = messages[messageIndex];
const listItem = document.createElement('li');
listItem.appendChild(document.createTextNode(message));
errorsList.appendChild(listItem);
}
const inputList = this.form.querySelectorAll(
'*[name=' + key + "], *[name='" + key + "[]']"
);
for (let inputIndex = 0; inputIndex < inputList.length; inputIndex++) {
const input = inputList[inputIndex];
input.classList.add('this-field-has-errors');
input.parentElement.appendChild(errorsList);
}
}
});
});
Stripe Payments Events
To style the Freeform Stripe credit card fields, you'll need to add a listener for the freeform-stripe-styling
event like this (example for Bootstrap):
const form = document.getElementById('my-form');
form.addEventListener("freeform-stripe-styling", function (event) {
event.detail.base = {
fontSize: "16px",
fontFamily: "-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\"",
}
})
});
Date Picker Events
The Freeform Date & Time field comes packed with a built-in, optional flatpickr instance. Check out the Date Picker Events documentation for full customization options of the flatpickr instance.