Choice Counter

Get the total number of checkboxes checked or multi-select options selected. Useful when wanting to apply conditional logic based on those totals.

Instructions

Code

Filename: gw-choice-counter.php

<?php
/**
 * Gravity Wiz // Gravity Forms // Choice Counter
 *
 * Instruction Video: https://www.loom.com/share/0a5e502b49b34031bc7ed73e56dbae68
 *
 * Get the total number of checkboxes checked or multi-select options selected. Useful when wanting to apply conditional
 * logic based on those totals.
 *
 * @version   1.3
 * @author    David Smith <david@gravitywiz.com>
 * @license   GPL-2.0+
 * @link      https://gravitywiz.com/
 */
class GW_Choice_Count {

	private static $is_script_output;

	private $_args = array();

	function __construct( $args ) {

		$this->_args = wp_parse_args( $args, array(
			'form_id'          => false,
			'count_field_id'   => false,
			'choice_field_id'  => null,
			'choice_field_ids' => false,
			'values'           => false,
		) );

		if ( isset( $this->_args['choice_field_id'] ) ) {
			$this->_args['choice_field_ids'] = array( $this->_args['choice_field_id'] );
		}

		add_filter( 'gform_pre_render', array( $this, 'load_form_script' ), 10, 2 );
		add_action( 'gform_register_init_scripts', array( $this, 'add_init_script' ) );
		//add_action( 'gform_pre_validation',        array( $this, 'override_submitted_value') );

	}

	public function load_form_script( $form, $is_ajax_enabled ) {

		if ( $this->is_applicable_form( $form ) && ! has_action( 'wp_footer', array( $this, 'output_script' ) ) ) {
			add_action( 'wp_footer', array( $this, 'output_script' ) );
			add_action( 'gform_preview_footer', array( $this, 'output_script' ) );
			add_action( 'admin_footer', array( $this, 'output_script' ) );
		}

		return $form;
	}

	function output_script() {
		?>

		<script type="text/javascript">

			( function( $ ) {

				window.GWChoiceCount = function( args ) {

					var self = this;

					// Copy all args to current object: formId, fieldId.
					for ( prop in args ) {
						if ( args.hasOwnProperty( prop ) ) {
							self[ prop ] = args[prop];
						}
					}

					self.updateEventHandlers = function() {
						for( var i = 0; i < self.choiceFieldIds.length; i++ ) {

							var choiceFieldId       = self.choiceFieldIds[i],
								choiceFieldSelector = '#input_' + self.formId + '_' + choiceFieldId,
								$choiceField        = $(choiceFieldSelector),
								$parentForm         = $choiceField.parents('form');

							$parentForm.off( 'click', choiceFieldSelector, self.updateChoiceEventHander );
							$parentForm.off( 'change', choiceFieldSelector, self.updateChoiceEventHander );

							if ( self.isCheckableField( $choiceField ) ) {
								$parentForm.on( 'click', choiceFieldSelector, self.updateChoiceEventHandler );
							} else {
								$parentForm.on( 'change', choiceFieldSelector, self.updateChoiceEventHandler );
							}

						}
					};

					// Event handler for all listeners to avoid DRY and to maintain a pointer reference to the function
					// which we can use to explicity unbind event handlers
					self.updateChoiceEventHandler = function() {
						self.updateChoiceCount( self.formId, self.choiceFieldIds, self.countFieldId, self.values );
					};

					self.init = function() {

						self.updateEventHandlers();
						self.updateChoiceEventHandler();

						// Listen for `gppa_updated_batch_fields` and update count as GPPA may have re-written choice fields
						$( document ).on( 'gppa_updated_batch_fields', function( e, formId ) {
							if ( parseInt( formId ) === self.formId ) {
								self.updateEventHandlers();
								self.updateChoiceEventHandler();
							}
						} );
					};

					self.isCheckableField = function($field ) {
						return Boolean( $field.find( ':checkbox, :radio' ).length );
					}

					self.updateChoiceCount = function( formId, choiceFieldIds, countFieldId, values ) {

						var countField = $( '#input_' + formId + '_' + countFieldId ),
							count      = 0;

						// Prevent count field from being recalculated if it's hidden.
						if ( ! gformIsHidden( countField ) ) {
							for ( var i = 0; i < choiceFieldIds.length; i++ ) {

								var $choiceField = $( '#input_' + formId + '_' + choiceFieldIds[ i ] );
								if ( ! values ) {
									// If no values provided in the config, just get the number of checkboxes checked.
									if ( self.isCheckableField( $choiceField ) ) {
										count += $choiceField.find( ':checked' ).not(':disabled').not(' #choice_' + choiceFieldIds[ i ] + '_select_all').length;
									} else {
										count += $choiceField.find( 'option:selected' ).length;
									}
								} else {
									// When values are provided, match the values before adding them to count.
									var selectedValues = [];
									$choiceField.find( ':checked' ).each( function( k, $selectedChoice ) {
										selectedValues.push( $selectedChoice.value );
									});
									values.forEach( function( val ) {
										count += selectedValues.indexOf( val ) >= 0;
									});
								}

							}

							if( parseInt( countField.val() ) != parseInt( count ) ) {
								countField.val( count ).change();
								countField[0].dispatchEvent( new Event( 'change', { bubbles: true } ) );
							}
						}

					};

					// Recalculate Count field when it is revealed by conditional logic.
					gform.addAction( 'gform_post_conditional_logic_field_action', function ( formId, action, targetId ) {
						var id = targetId.split( '_' ).pop();
						if ( id == args.countFieldId && action === 'show' ) {
							self.updateChoiceEventHandler();
						}
					} );

					self.init();

				}

			} )( jQuery );

		</script>

		<?php
		self::$is_script_output = true;
	}

	function add_init_script( $form ) {

		if ( ! $this->is_applicable_form( $form['id'] ) ) {
			return;
		}

		$args = array(
			'formId'         => $this->_args['form_id'],
			'countFieldId'   => $this->_args['count_field_id'],
			'choiceFieldIds' => $this->_args['choice_field_ids'],
			'values'         => $this->_args['values'],
		);

		$script = 'new GWChoiceCount( ' . json_encode( $args ) . ' );';
		$slug   = implode( '_', array( 'gw_choice_count', $this->_args['form_id'], $this->_args['count_field_id'] ) );

		GFFormDisplay::add_init_script( $this->_args['form_id'], $slug, GFFormDisplay::ON_PAGE_RENDER, $script );

		return;
	}

	function override_submitted_value( $form ) {
		//$_POST["input_{$this->count_field_id}"] = $day_count;
		return $form;
	}

	public function is_applicable_form( $form ) {

		$form_id = isset( $form['id'] ) ? $form['id'] : $form;

		return empty( $this->_args['form_id'] ) || intval( $form_id ) === intval( $this->_args['form_id'] );
	}

	public function is_ajax_submission( $form_id, $is_ajax_enabled ) {
		return isset( GFFormDisplay::$submission[ $form_id ] ) && $is_ajax_enabled;
	}

}

# Configuration

new GW_Choice_Count( array(
	'form_id'          => 123,           // The ID of your form.
	'count_field_id'   => 4,             // Any Number field on your form in which the number of checked checkboxes should be dynamically populated; you can configure conditional logic based on the value of this field.
	'choice_field_ids' => array( 5, 6 ), // Any array of Checkbox or Multi-select field IDs which should be counted.
	'values'           => false,         // Specify an array of values that should be counted. Values not in this list will not be counted. Defaults to `false` which will count all values.
) );

Comments

  1. Daniel Turner
    Daniel Turner March 3, 2025 at 6:23 pm

    Hi! This is great, I am having one issue, I am trying to count the number of checkboxes and then use that as the qty for the product, the checkboxes count and it updated the qty field, but it does not update the form total. If I manually type the qty in the form total updates. What might I be doing wrong?

    Reply
    1. Samuel Bassah
      Samuel Bassah Staff March 4, 2025 at 3:56 am

      Hi Daniel,

      I was able to recreate the issue you’re experiencing. I’ve forwarded the ticket to our developers to look into it. We’ll update this comment when the issue is resolved.

      Best,

    2. Saif Sultan
      Saif Sultan Staff March 12, 2025 at 2:29 am

      Hi Daniel,

      We have fixed the issue on the snippet, can you please try the update from the link above. 🙂

      Feel free to let us know if you have any questions.

      Best,

Leave a Reply

Your email address will not be published. Required fields are marked *

  • Trouble installing this snippet? See our troubleshooting tips.
  • Need to include code? Create a gist and link to it in your comment.
  • Reporting a bug? Provide a URL where this issue can be recreated.

By commenting, I understand that I may receive emails related to Gravity Wiz and can unsubscribe at any time.