Gravity Forms is not only THE way to create and manage forms in WordPress, but is also pretty awesome when it comes to extensibility and flexibility. However, as most software, it has its issues. One of those is how it outputs some of the JavaScript, which in certain cases will break your site. This is how to fix it.

This week I was helping out on a site that handled everything right when it comes to JavaScript: All of it was concatenated, minified and loaded in the footer. But this site also needed a form to be loaded programmatically with the gravity_form() function and using AJAX to handle the submission. To my horror, Gravity Forms outputs quite a lot of jQuery dependent JavaScript right after the form element. This is bad considering best practices for fast loading, but was a total failure considering that jQuery wouldn’t be loaded by the time and causing the form to malfunction.

[bjornad]

Actually, as you can see from this screenshot, is that there are two blocks of JavaScript in the source: One that handles the form submission, and one that initializes the form:

Gravity Forms inline JavaScript

Luckily, as Gravity Forms have a lot of filter and action hooks available, the second block is easily moved to the footer with a single line:

add_filter('gform_init_scripts_footer', '__return_true');

But for the first block, we’re not so lucky. It is all part of the long concatenated HTML output that GF produces:

Gravity Forms concatenated $form_string

But if you look closely, there is in fact filter hooks both in the beginning and in the end of that JavaScript section. With a little clever trickery we can encapsulate that in a JS event callback that won’t run until after the content of the page have loaded:

add_filter( 'gform_cdata_open', 'wrap_gform_cdata_open' );
function wrap_gform_cdata_open( $content = '' ) {
	$content = 'document.addEventListener( "DOMContentLoaded", function() { ';
	return $content;
}
add_filter( 'gform_cdata_close', 'wrap_gform_cdata_close' );
function wrap_gform_cdata_close( $content = '' ) {
	$content = ' }, false );';
	return $content;
}

This won’t help us with the load speed issue (that JS should be run in the footer because the browser halts all loading whenever a block is encountered), but at least the execution of the script block will be delayed until the DOMContentLoaded event is triggered – at which point jQuery will be loaded.