Gravity Forms - markhowellsmead/helpers GitHub Wiki

Add-ons

CSS

Special styling

Change CSS file loading order

Gravity Forms often sticks the CSS link tags directly in the body of the page. And only on pages where a form has been placed using a shortcode or by a direct PHP call.

This can cause problems with specificity. It's better to load Gravity Forms CSS files in the head and then enqueue your CSS files after them.

Or just stop using their CSS altogether.

Dequeue Gravity Forms CSS for all forms

add_action('gform_enqueue_scripts', [$this, 'dequeueGravityFormsCSS'], 11);

with

public function dequeueGravityFormsCSS()
{
	wp_dequeue_style('gforms_reset_css');
	wp_dequeue_style('gforms_datepicker_css');
	wp_dequeue_style('gforms_formsmain_css');
	wp_dequeue_style('gforms_ready_class_css');
	wp_dequeue_style('gforms_browsers_css');
}

Move Gravity Forms CSS files to the page HEAD

add_action('wp_enqueue_scripts', function(){
	if (function_exists('gravity_form_enqueue_scripts')) {
		gravity_form_enqueue_scripts(1);
	}
});

Enqueue your CSS files after the Gravity Forms CSS files

wp_enqueue_style('myThemeCSS', get_template_directory_uri() . '/theme.css', ['gforms_reset_css', 'gforms_formsmain_css', 'gforms_ready_class_css', 'gforms_browsers_css'], '1.0.0');

Create user from GF submission

Replace AJAX spinner icon

The following replaces the standard icon with a 1x1px transparent GIF. The CSS adds a spinning animation and a border.

add_filter('gform_ajax_spinner_url', function(){
	return  'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
}, 10);

Don't forget to autoprefix.

.gform_ajax_spinner {
	border: 2px solid rgba(0, 0, 0, 0.1);
	border-left: 2px solid silver;
	animation: gf_ajaxspinner 1.1s infinite linear;
	border-radius: 50%;
	width: 1.5rem;
	height: 1.5rem;
	flex-basis: 1.5rem; // if necessary
	margin-left: 0.5rem;
	padding: 0;
}
@keyframes gf_ajaxspinner {
	0% {
		transform: rotate(0deg);
	}
	100% {
		transform: rotate(360deg);
	}
}

Block AJAX to allow styling of the spinner

Don't forget to remove this when you're finished.

add_action('gform_pre_submission', function () {
	die('x');
}, 10);

Create custom field

To create a custom Gravity Forms field, you need to add a button to the backend interface, so that the user can pull the field into the form configuration. Then you need to define a (single) custom field function which outputs the HTML in both the frontend and backend. Use the helper function GFCommon::is_form_editor() to switch HTML outputs for frontend and backend.

In the following code, we'll add a custom checkbox checkbox_ct_terms, which has a special function.

/**
 * Add the custom field as a button to the available fields in BE
 */
add_filter('gform_add_field_buttons', function ($field_groups) {
	foreach ($field_groups as &$group) {
		if ($group['name'] == 'advanced_fields') {
			$group['fields'][] = array(
				'class' => 'button',
				'value' => __('Nutzungsbest.', 'wptheme.sage'),
				'onclick' => 'StartAddField(\'checkbox_ct_terms\');'
			);
			break;
		}
	}
	return $field_groups;
});

/**
 *  Output the field HTML (used in FE and BE)
 *  The call GFCommon::is_form_editor() allows you to return different output for FE and BE
 */
add_action('gform_field_input', function ($input, $field, $value, $lead_id, $form_id) {

	if ($field['type'] == 'checkbox_ct_terms') {
		$extra_css = isset($field['cssClass']) ? $field['cssClass'] : ”;
		return sprintf(
			'<div class="ginput_container ginput_container_checkbox ginput_container_checkbox_ct_terms %5$s">
				<ul class="gfield_checkbox" id="input_1_%1$s">
					<li class="gchoice_1_%1$s_1">
						<input %6$s name="input_%1$s.1" type="checkbox" value="%3$s" id="choice_1_%1$s_1" tabindex="%2$s" required="true">
						<label for="choice_1_%1$s_1" id="label_1_%1$s_1" class="gfield_label">%4$s</label>%7$s
					</li>
				</ul>
			</div>',
			$field['id'],
			GFCommon::get_tabindex(),
			__('Ich akzeptiere die Nutzungsbedingungen und rechtlichen Hinweise.', 'wptheme.sage'), // field value
			__('Ich akzeptiere die Nutzungsbedingungen und rechtlichen Hinweise. <span class="gfield_required">*</span>', 'wptheme.sage'), // field label
			$extra_css,
			GFCommon::is_form_editor() ? 'disabled' : '',
			filter_var($field['checkbox_ct_terms_field_url'], FILTER_VALIDATE_URL) ? sprintf(
				' (<a class="gfield_checkbox_more_link" href="%1$s" target="_blank">%2$s</a>)',
				$field['checkbox_ct_terms_field_url'],
				__('Mehr dazu', 'wptheme.sage')
			) : (GFCommon::is_form_editor() ? '<em>' .__('Ungültige URL eingetragen!', 'wptheme.sage').'</em>' : '')
		);
	}

	return $input;
}, 10, 5);

Custom settings field

To add a new settings field option to the custom field you've created, you need to hook into gform_field_standard_settings. (See https://www.gravityhelp.com/documentation/category/field-settings/ for other settings groups.)

/**
 * Add custom backend settings field to a Gravity Forms field for a URL.
 * https://engineering.growella.com/gravity-forms-custom-field-settings/
 */
add_action('gform_field_standard_settings', function ($position) {

	if (100 !== $position) {
		return;
	}

	printf(
		'<li class="checkbox_ct_terms_field_url_setting field_setting">
			<ul>
				<li>
					<label for="checkbox_ct_terms_field_url" class="section_label">%1$s</label>
					<input id="checkbox_ct_terms_field_url" type="url" style="width: 100%%" onchange="SetFieldProperty(\'checkbox_ct_terms_field_url\', this.value)" />
				</li>
			</ul>
		</li>',
		esc_html('URL', 'wptheme.sage')
	);
});

You assign permissions - that the settings field may appear in the backend editor - using a JavaScript snippet, which is embedded directly after the settings field.

/**
 * Add an inline JS link below the field in BE, which initialises the field's backend functionality
 */
add_action('gform_editor_js', function () {
	echo '<script src="' .get_template_directory_uri(). '/dist/lib/js/gf_checkbox_ct_terms.js"></script>';
});

The content of that file is as follows (in this example). You're allowing the custom settings field checkbox_ct_terms_field_url by adding it to the fieldSettings array entry for the custom field checkbox_ct_terms.

var fieldSettings;

(function ($) {

	$(document).ready(function ($) {

		// Which subfields are allowed on this field type?
		fieldSettings.checkbox_ct_terms. = '.checkbox_ct_terms_field_url_setting, .admin_label_setting, .label_setting, .error_message_setting, .css_class_setting';

		// Fill the stored value of the checkbox_ct_terms_field_url subfield
		$(document).bind('gform_load_field_settings', function (event, field, form) {
			$('#checkbox_ct_terms_field_url').val(field['checkbox_ct_terms_field_url']);
		});
	});

}(jQuery));

Include custom field value in confirmation messages

/**
 * Ensure value of custom field 'checkbox_ct_terms' is included in confirmation messages ({all_fields})
 */
add_filter('gform_merge_tag_filter', function ($value, $merge_tag, $modifier, $field, $raw_value) {
	switch ($field->type) {
		case 'checkbox_ct_terms':
			if (empty($value) && isset($_POST['input_' .$field['id']. '_' .$field['formId']])) {
				$value = esc_attr($_POST['input_' .$field['id']. '_' .$field['formId']]);
			}
			break;
	}
	return $value;
}, 10, 5);

Add CSS class to wrapper

Extends the list of class names on the overall field container - usually li.gfield - with a class name indicating the type of field.

use DOMDocument;

…

add_filter('gform_field_container', [$this, 'addFieldTypeClass'], 10, 2);

…

/**
 * Add a field type class name to every field
 *
 * @param string $html
 * @param mixed $field
 * @return void
 */
public function addFieldTypeClass(string $html, mixed $field)
{

	libxml_use_internal_errors(true);
	$document = new DOMDocument();
	$document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));

	$class = $document->documentElement->childNodes[0]->childNodes[0]->getAttribute('class');

	$document->documentElement->childNodes[0]->childNodes[0]->setAttribute('class', "{$class} gfield_container--{$field->type}");

	$body = $document->saveHtml($document->getElementsByTagName('body')->item(0));
	return str_replace(['<body>', '</body>'], '', $body);
}

Scroll to first error message, with offset

Version without Real Time Validation:

(function($) {

	/**
	 * Scroll to the first error message (respecting an offset)
	 * this requires only jQuery, not require any additional plugins
	 */
	$(document).bind('gform_post_render', function() {
		var $firstError = $('li.gfield.gfield_error:first');
		if ($firstError.length > 0) {
			$firstError.find('input, select, textarea').eq(0).focus();
			var scrollTo = $firstError.offset().top;
			if ($('.c-toolbar:first').length) {
				scrollTo -= $('.c-toolbar').outerHeight(true) * 1.2;
			}
			$('html, body').stop().animate({
				scrollTop: scrollTo
			}, 1000, 'swing');
		}
	});
})(jQuery);

Version with Real Time Validation for Gravity Forms:

$(window).on('load.formoffset resize.formoffset', function() {
	if($('.ct-signup-form').length > 0){
		window.lv_offset = function(currentOffset) {
			return $('.ct-signup-form .gfield.gfield_error').first().offset().top;
		};
	}
});

Real Time Validation for Gravity Forms

Custom error messages

/**
 * Customize the inline error messages for the Gravity Forms Real Time Validation plugin
 * @param  array $messages The default messages
 * @return array           The potentially amended messages
 */
public function errorMessages($messages)
{
	foreach (array_keys($messages) as $key) {
		$messages[$key] = _x('Dies ist ein Pflichtfeld und muss ausgefüllt werden', 'Default custom live form validation message', 'wptheme-starter');
	}
	return $messages;
}

hooked to

add_filter('lv_default_error_messages', [$this, 'errorMessages'], 10, 1);

Dynamically fill (or select) value

Wrap checkbox and radio choice text

add_filter('gform_field_choices', [$this, 'checkboxChoices'], 10, 2);

…

public function checkboxChoices($choices, $field)
{
	if ($field->type !== 'checkbox' && $field->type !== 'radio') {
		return $choices;
	}

	libxml_use_internal_errors(true);

	$domDocument = new DOMDocument();
	$domDocument->loadHTML(mb_convert_encoding($choices, 'HTML-ENTITIES', 'UTF-8'));

	$xpath = new DOMXpath($domDocument);

	foreach ($xpath->query('//label') as $label) {

		$span = $domDocument->createElement('span');
		$span->setAttribute('class', 'gchoice_text o-formchoice__text');

		foreach ($label->childNodes as $child) {
			$span->appendChild($child->cloneNode(true));
		}

		while ($label->hasChildNodes()) {
			$label->removeChild($label->firstChild);
		}

		$label->appendChild($span);
	}

	$body = $domDocument->saveHtml($domDocument->getElementsByTagName('body')->item(0));
	return str_replace(['<body>', '</body>'], '', $body);
}
⚠️ **GitHub.com Fallback** ⚠️