By default WooCommerce checkout includes fields for address, post code, city and state, how ever when using a shipping method such as “Local pickup” there is no need to collect client address information.
Here’s some custom code that can be used to dynamically hide certain WC checkout fields based on which shipping method is selected.
Server-side
First, let’s define a function that determines whether some fields should be shown or not:
function current_shipping_method_requires_address() {
$chosen_methods = WC()->session->get('chosen_shipping_methods');
if(!is_array($chosen_methods) || empty($chosen_methods)) {
return true;
}
$chosen_shipping_method = $chosen_methods[0];
// Don't require address when "Local pickup" method is chosen.
if(strpos($chosen_shipping_method, 'local_pickup') !== false) {
return false;
}
return true;
}
In this case, we check for the presence of the string local_pickup
in the name of the current shipping method.
Now let’s utilize this function to make the fields optional when applicable:
/** Don't require fields when local pickup shipping method is selected. */
add_filter('woocommerce_billing_fields', function($fields) {
if(!current_shipping_method_requires_address()) {
foreach([
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
'billing_state',
] as $field) {
$fields[$field]['required'] = false;
}
}
return $fields;
});
Now whenever the local pickup shipping method is selected, the checkout form submission will succeed even if the specified billing info is not filled in.
To apply this functionality for other shipping methods, simply inspect your checkout form shipping section to find the IDs of shipping methods:
and then alter the current_shipping_method_requires_address
for the desired outcome.
Client-side
While the address-related fields are no longer required, they are still visible in the checkout form and they even have a red asterisk indicating that the fields are required.
To solve this problem you essentially need to reproduce the logic on the front-end as well.
The currentShippingMethodRequiresAddress
is a JavaScript equivalent of the current_shipping_method_requires_address
PHP function:
const currentShippingMethodRequiresAddress = () => {
const data = Object.fromEntries(new FormData($('form[name="checkout"]')[0]).entries())
const shippingMethod = data['shipping_method[0]']
if(shippingMethod.indexOf('local_pickup') !== -1) {
return false
}
return true
}
Then, using this function we can add the invisible
class to fields that shouldn’t be visible, using the updated_checkout
hook to make sure the changes are reflected when the user changes the shipping method, for example:
const ADDRESS_FIELDS = [
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
'billing_state',
]
const updateFieldsVisibility = () => {
if(currentShippingMethodRequiresAddress()) {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).removeClass('invisible')
})
} else {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).addClass('invisible')
})
}
}
$(document.body).on('updated_checkout', updateFieldsVisibility)
Then we’ll also need some CSS for the .invisible
class to have any effect:
wp_add_inline_style('woocommerce-inline', '.form-row.invisible:not(#_) { display: none !important; }');
Here is the whole client-side code put together, such that it is only enqueued on the checkout page and the relevant JS and CSS additions are added using wp_add_inline_script
and wp_add_inline_style
respectively:
/** Hide hidden fields client-side. */
add_action('woocommerce_checkout_init', function() {
add_action('wp_enqueue_scripts', function() {
$js = <<<JS
;jQuery && jQuery($ => {
const currentShippingMethodRequiresAddress = () => {
const data = Object.fromEntries(new FormData($('form[name="checkout"]')[0]).entries())
const shippingMethod = data['shipping_method[0]']
if(shippingMethod.indexOf('local_pickup') !== -1) {
return false
}
return true
}
const ADDRESS_FIELDS = [
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
'billing_state',
]
const updateFieldsVisibility = () => {
if(currentShippingMethodRequiresAddress()) {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).removeClass('invisible')
})
} else {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).addClass('invisible')
})
}
}
$(document.body).on('updated_checkout', updateFieldsVisibility)
});
JS;
wp_add_inline_script('wc-checkout', $js, 'after');
wp_add_inline_style('woocommerce-inline', '.form-row.invisible:not(#_) { display: none !important; }');
}, 100);
});
Complete code
Here is the final code put together, which you can place in your child theme’s functions.php
file, for example.
function current_shipping_method_requires_address() {
$chosen_methods = WC()->session->get('chosen_shipping_methods');
if(!is_array($chosen_methods) || empty($chosen_methods)) {
return true;
}
$chosen_shipping_method = $chosen_methods[0];
// Don't require address when "Local pickup" method is chosen.
if(strpos($chosen_shipping_method, 'local_pickup') !== false) {
return false;
}
return true;
}
/** Don't require fields when local pickup shipping method is selected. */
add_filter('woocommerce_billing_fields', function($fields) {
if(!current_shipping_method_requires_address()) {
foreach([
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
'billing_state',
] as $field) {
$fields[$field]['required'] = false;
}
}
return $fields;
});
/** Hide hidden fields client-side. */
add_action('woocommerce_checkout_init', function() {
add_action('wp_enqueue_scripts', function() {
$js = <<<JS
;jQuery && jQuery($ => {
const currentShippingMethodRequiresAddress = () => {
const data = Object.fromEntries(new FormData($('form[name="checkout"]')[0]).entries())
const shippingMethod = data['shipping_method[0]']
if(shippingMethod.indexOf('local_pickup') !== -1) {
return false
}
return true
}
const ADDRESS_FIELDS = [
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
'billing_state',
]
const updateFieldsVisibility = () => {
if(currentShippingMethodRequiresAddress()) {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).removeClass('invisible')
})
} else {
ADDRESS_FIELDS.forEach(function(id) {
$(`#\${id}_field`).addClass('invisible')
})
}
}
$(document.body).on('updated_checkout', updateFieldsVisibility)
});
JS;
wp_add_inline_script('wc-checkout', $js, 'after');
wp_add_inline_style('woocommerce-inline', '.form-row.invisible:not(#_) { display: none !important; }');
}, 100);
});
You can modify which shipping methods the code applies for and which fields are hidden, but the changes should be applied to both the server-side and client-side code.
Leave a Reply