Loading jQuery Immediately before Gravity Forms

May 18, 2015

Loading jQuery asychronously or in the footer on pages with ajax-enabled Gravity Forms is tricky because the form relies on inlined jQuery-dependent scripts that immediately follow it in the DOM:

Inline jQuery-dependent Gravity Form scripts

In this case, the typical gform_init_scripts_footer solution doesn’t work, because moving jQuery to the footer would break these inline scripts.

Another solution I’ve run across uses gform_cdata_open and gform_cdata_close to wrap these inline scripts in a function that’s called once jQuery has been loaded. However, this clever technique can generate errors on payment forms (e.g. Uncaught ReferenceError: gf_global is not defined) due to the scripts being run after DOMContentLoaded instead of before as Gravity Form expects.

Ultimately, the most reliable (and performant) solution I’ve found is to inject a synchronous jQuery <script> immediately before the inline scripts using the gform_cdata_open hook once.

/**
 * Deregister jQuery in <head>, so it can be loaded in the footer or before the 
 * first Gravity Form on the page. 
 */
function gc_deregister_default_jquery() 
{
	wp_deregister_script('jquery');
}
add_action( 'wp_enqueue_scripts', 'gc_deregister_default_jquery' );

/**
 * Inject synchronous jQuery dependency before the Gravity Form inline scripts
 */
function inject_jquery_above_gravity_form( $content = '' ) 
{
	// keep track of jquery so it's not loaded twice!
	global $jquery_already_injected;
	
	if ( !isset($jquery_already_injected) ) {
		
		$jquery_already_injected = true;

		// End inline script
		$content .= "</script>\n";

		// Inject jQuery
		$content .= "<script src='/path/to/jquery.min.js'></script>\n";		

		// Start inline script again
		$content .= "<script>";
	}

	return $content;
}
add_filter( 'gform_cdata_open', 'inject_jquery_above_gravity_form' );

/**
 * Enqueue jQuery in footer unless it's already been injected above Gravity Form.
 * In this case, enqueue a fake version to trigger dependent scripts, and then remove this fake version.
 */

function enqueue_jquery()
{
	global $jquery_already_injected;

	// jQuery has not been loaded
	if ( !isset($jquery_already_injected) ) {
		wp_enqueue_script('jquery');
	}
	// jQuery has already been loaded above a Gravity Form
	else {
		
		// Enqueue fake script to trigger dependencies
		wp_enqueue_script( 'jquery', '//fake-jquery-script.js', [], null );

		// Remove this fake script's HTML before it's actually injected into the DOM
		function gc_remove_fake_jquery_script( $tag ) {
			$tag = ( strpos($tag, 'fake-jquery-script') !== false ) ? '' : $tag;
			return $tag;
		}
		add_filter( 'script_loader_tag', 'gc_remove_fake_jquery_script' );
	}

}
add_action('wp_footer', 'enqueue_jquery', 9);

Your comments and feedback are welcome!

Comments are welcome!