Hide Checkout Items Based on Shipping Method

WooCommerce conditional checkout options tutorial

A copy-and-paste tutorial that details how to hide/show elements on the WooCommerce checkout page, depending on which shipping method the customer chooses.

In this example…

  • we’ll hide the Shipping Address fields when the customer selects the “Local Pickup” (Collect from store) option.
  • we’ll hide the Request a WooCommerce Quote button when the customer selects “Flat Rate International shipping”.

info Make sure you’re using a custom child theme so you can edit your “functions.php” file.

WooCommerce checkout hide shipping address
Hide the shipping address fields when “Collect from store” is selected

How it will work

The WooCommerce checkout page is dynamic – lots of content is loaded & reloaded in the browser (using Ajax calls) as the customer enters their billing fields. This means we need to put our hide/show logic into the browser as JavaScript.

We’ll hook the WooCommerce JavaScript event that fires when the checkout updates itself, “updated_checkout”. Our event handler will figure out which shipping method is selected and then add a class (based on the shipping method) to the document’s body element. So if the customer picks “Collect from store”, we’ll add a class called “shipping-collect-in-store” to the body element like this.

<body class="woocommerce-checkout ...... shipping-collect-in-store">...

The difficulty here is that WooCommerce stores your site’s shipping method instances against zones using identifiers like “flat_rate:3” and “local_pickup:9”. Those are too cryptic, so we need to add some back-end code to grab our shipping method names and turn them into slugs… which we can then turn into nice CSS class names. We’ll do that bit in PHP.

Grab the shipping methods in PHP

Start by creating a PHP file in your custom child theme called wpt-conditional-checkout-options.php, then paste the following into it:

<?php

/**
 * Headwall WP Tutorials : Conditional Checkout Options (WPTCCO)
 *
 * https://wp-tutorials.tech/refine-wordpress/hide-checkout-items-based-on-shipping-method/
 */

defined('WPINC') || die();

/**
 * The get_zones() function does not include the default (rest-of-world)
 * shipping zone. This switch lets us add it to the result of get_zones().
 */
const WPTCCO_INCLUDE_REST_OF_WORLD_ZONE = true; // false

/**
 * Loop through each shipping zone, and each zone's shipping methods to create a
 * list of shipping method ids and slugs we can use in the front-end code.
 */
function wptcco_get_shipping_rate_metas() {
	$shipping_rate_metas = [];

	if (class_exists('WC_Shipping_Zones')) {
		$zones = WC_Shipping_Zones::get_zones();

		// Maybe add the default zone (zone id = 0)
		if (WPTCCO_INCLUDE_REST_OF_WORLD_ZONE) {
			$default_zone = new WC_Shipping_Zone(0);
			$zones[$default_zone->get_id()] = $default_zone->get_data();
			$zones[$default_zone->get_id()]['zone_id'] = $default_zone->get_id();
			$zones[$default_zone->get_id()]['formatted_zone_location'] = $default_zone->get_formatted_location();
			$zones[$default_zone->get_id()]['shipping_methods'] = $default_zone->get_shipping_methods(false);
		}

		foreach ($zones as $zone_id => $zone) {
			foreach ($zone['shipping_methods'] as $instance_id => $shipping_method) {
				// e.g. flat_rate:3
				$unique_id = $shipping_method->get_rate_id();

				// e.g. "Flat rate international"
				$title = $shipping_method->get_title();

				// e.g. "flat-rate-international"
				$slug = sanitize_title($title);

				$shipping_rate_metas[$unique_id] = [
					'zoneId' => $zone_id,
					'slug' => $slug,
					'title' => $title,
				];
			}
		}
	}

	return $shipping_rate_metas;
}

/**
 * On the checkout page, inject our JS and CSS into the header, and pass the
 * zone/shipping metas into the wpctcco globlas JS object.
 */
function wptcco_enqueue_scripts() {
	if (function_exists('is_checkout') && is_checkout()) {
		$theme_version = wp_get_theme()->get('Version');
		$base_uri = get_stylesheet_directory_uri();

		wp_enqueue_style(
			'wptcco', // Stylesheet handle
			$base_uri . '/wpt-conditional-checkout-options.css',
			null, // No style dependencies
			$theme_version
		);

		wp_enqueue_script(
			'wptcco', // Script handle
			$base_uri . '/wpt-conditional-checkout-options.js',
			['jquery'], // WooCommerce checkout relies on jQuery
			$theme_version
		);

		wp_localize_script(
			'wptcco', // Script handle should match that from wp_enqueue_script()
			'wptcco', // This will be the name of our global JavaScript object
			[
				'shippingRates' => wptcco_get_shipping_rate_metas(),
			]
		);
	}
}
add_action('wp_enqueue_scripts', 'wptcco_enqueue_scripts');

See that we’ve got a function called wptcco_get_shipping_rate_metas() that loops over the shipping zones, and shipping methods, to return an array of internal ids (like “local_pickup:3”). These internal ids are mapped to their names and slugs (e.g. “Collect from store” and “collect-from-store”).

Open your child theme’s functions.php file and add the following couple of lines:

// Headwall WP Tutorials : Conditional Checkout Components
require_once dirname(__FILE__) . '/wpt-conditional-checkout-options.php';

Finally, create these two empty files:

  • wpt-conditional-checkout-options.css
  • wpt-conditional-checkout-options.js

Save everything, go to your site, put something into your cart and go to the checkout page.

Detect the current shipping method in JavaScript

Open wpt-conditional-checkout-options.js and paste the following into it:

/**
 * Headwall WP Tutorials : Conditional Checkout Options (WPTCCO)
 *
 * https://wp-tutorials.tech/refine-wordpress/hide-checkout-items-based-on-shipping-method/
 */

// Diagnostics
// console.log('Conditional checkout options : load');

(function($) {
	'use strict';

	wptcco.previousShippingMethodClass = '';

	wptcco.init = () => {
		// Diagnostics
		// console.log('Conditional checkout options : init');

		// Initial configuration
		wptcco.shippingMethodChangedHandler();

		// Whenever WooCommerce triggers the "updated_checkout" event, call our
		// shippingMethodChangedHandler() function.
		$(document.body).on('updated_checkout', wptcco.shippingMethodChangedHandler);
	};

	/**
	 * Our core function is an event-handler for the "updated_checkout" event.
	 * 
	 * If we've already added a class to the body element on a previous iteration,
	 * remove it.
	 *
	 * If the user has selected a shipping method (or only one method is available),
	 * find its slug, append "shipping-" to the beginning of the slug and then add
	 * it to the body element.
	 */
	wptcco.shippingMethodChangedHandler = (event) => {
		// Diagnostics
		// console.log('Conditional checkout options : updated_checkout');

		let newShippingMethodClass = '';
		const shippingMethodRadioButton = $('#shipping_method input[type="radio"].shipping_method[checked], #shipping_method input[type="hidden"]');
		if (shippingMethodRadioButton.length === 1) {
			// e.g. flat_rate:3
			const uniqueId = shippingMethodRadioButton.val();

			// Diagnostics
			// console.log(`Selected shipping rate: ${uniqueId}`);

			if (typeof wptcco.shippingRates[uniqueId] !== 'undefined') {
				const shippingRateMeta = wptcco.shippingRates[uniqueId];

				// Diagnostics
				// console.log(shippingRateMeta);

				newShippingMethodClass = `shipping-${shippingRateMeta.slug}`;
			}
		}

		// Diagnostics
		// console.log(`Previous: ${wptcco.previousShippingMethodClass}`);
		// console.log(`New: ${newShippingMethodClass}`);

		// If we've been here before, remove the class we added last time.
		if (wptcco.previousShippingMethodClass) {
			document.body.classList.remove(wptcco.previousShippingMethodClass);
		}

		// Add the new class (if it's not an empty string).
		if (newShippingMethodClass) {
			document.body.classList.add(newShippingMethodClass);
		}

		wptcco.previousShippingMethodClass = newShippingMethodClass;
	};

	/**
	 * Main entry point
	 */
	$(window).on('load', wptcco.init);

})(jQuery);

When you take out the empty lines and diagnostic comments, there’s not much to the code. All we do is figure out a CSS Class name we can use to represent the current shipping method, then add it to the body element.

If you want to examine the code in more detail and see it “working”, uncomment the diagnostics, open your browser’s Dev Tools tab and reload the checkout page.

When you switch between the available shipping methods, you should see the body classes changing right there in the DOM.

Shipping method represented as a CSS class in the body element
Monitor the DOM with your browser’s Dev Tools

Hide & Show the shipping address fields

I’ve given my shipping methods the following names:

  • Collect from store
  • Delivery
  • Flat Rate International
WooCommerce shipping zones
WooCommerce shipping zones, including the default zone

The sanitize_title() function in our PHP wptcco_get_shipping_rate_metas() function converts these into the following slugs for us:

  • collect-from-store
  • delivery
  • flat-rate-international

We pick these up in wptcco.shippingRates within shippingMethodChangedHandler() – our “updated_checkout” event handler. We prefix the shipping method slug with “shipping-” before we add them to the body class, so when someone selects “Collect from store”, we actually add the “shipping-collect-from-store” class to the body element.

Now all we need to do is add the following to wpt-conditional-checkout-options.css:

/**
 * wpt-conditional-checkout-options.css
 */

/**
 * Hide the shipping address fields when the shipping method
 * is set to local_pickup
 */
body.shipping-collect-from-store .woocommerce-shipping-fields {
  display: none;
}

/**
 * Hide the Request a Quote button when the shipping method
 * is set to flat_rate_international
 */
body.shipping-flat-rate-international .cto-request-a-quote-panel {
  display: none;
}

important If you’ve given your local pick-up shipping method a different title, you need to change your CSS selector accordingly. Just convert the shipping method’s title to lowercase and replace spaces with dashes.

important If you change the title of your shipping methods in the WooCommerce settings area, the shipping method slug will change, so the CSS class name will change too. So when you change anything to do with the checkout process… test the heck out of the checkout page afterwards.

Test and customise

Once the currently-selected shipping method is represented as a class in the body element, hiding and showing any page element is trivial. Use the examples above to get you started. If you find it’s not working as expected, use your browser’s Dev Tools to inspect the body element and make sure the class names are correct.

Have fun customising and tweaking your checkout experience 😎👍

Like This Tutorial?

Let us know

WordPress plugins for developers

Leave a comment