Large and Laggy Gravity Forms

Over at GravityView, I have the pleasure of working on Gravity Forms-related things and sometimes we have a performance issue on our hands. This time it was an issue with a form that has about 15 visible conditional fields, about 300 (yes, three, zero, zero, three hundred :)) hidden calculation fields and a product total at the end.

Slow Gravity Forms

This form was very laggy in the browser. Any interaction took around 3 seconds to propagate through, blocking the rendering thread completely.

Any click event, keyup event would result in a complete freeze of the browser on this form. Brainstorming began…

JavaScript profile on Gravity Forms

Thanks to some great people in the Russian WordPress community, lots of ideas were thrown, most of which “Just rewrite it using X” Carbon Fields, ACF or custom PHP and JavaScript. A rewrite is not good. Gravity Forms was chosen, GravityView went on top of that. Changing the forms now would mean weeks of custom work. Yes, 319 fields is huge, but there must be some other way!

Into the void with slow Gravity Forms functions

The main idea I was looking at was to locate the heavy function in JavaScript, try to proxy it into the void and only call it when needed. For, example, performance-intensive function expensive_func_a.

Save it for later var _expensive_func_a = expensive_func_a; and let it do nothing, like so:

expensive_func_a = function() { /** void */ };, and way when it’s called by all the events it’s bound to – nothing will really happen.

The function was located using the Performance profiler in Chrome’s Developer tools. Hefty little thing showed the resource gobbler.

Gravity Forms JavaScript Profiling

Ah. gformCalculateProductPrice. Fair enough, 300 calculation fields go into calculating the price, all in convoluted JavaScript with deferring, triggering and offloading. Fired up the console and wrote gformCalculateProductPrice = function() {};. The UI was instantly released, clicking, typing became possible again! Fantastic. But the calculation no longer happened.

The trick here would be do paginated forms then and place the final product pricing fields into the last page. Luckily the form was already broken into multiple pages and all we had to do was to run the noop code that voids the function and let it run on the last page where it’s really needed.

If breaking into multiple pages is not viable for one reason or another, the solution would be to proxy it (_proxy_gformCalculateProductPrice = gformCalculateProductPrice;), override the Submit button behavior to show “Calculate” first, which calls _proxy_gformCalculateProductPrice();, renames the button to “Submit” and allows the form to be submitted thereafter. It’s a bit more complicated, especially if the visitor wants to adjust some of the fields to get a lower price.

Either way, the final patch for the slow and laggy form looked like this:

add_action( 'gform_register_init_scripts_' . SLOW_FORM_ID, function( $form ) {
  // Do not void out on last page
  if ( rgpost( 'gform_target_page_number_' . SLOW_FORM_ID ) == SLOW_LAST_PAGE ) {
    <script type="text/javascript">
      // Into the void
      gformCalculateProductPrice = function() {};
} );

Brilliant! The form works, it’s much faster and everyone’s happy 🙂