Woocommerce Shipping Calculator Dynamic Country/State/Postal Code fields

shipping calculator

I have Woocommerce 2.1.12 with a shipping calculator on my cart page. I have it tied into the Fedex API to estimate shipping costs. Which means the user has to fill in Country, State/Province, Postal Code to calculate the Fedex shipping cost. One problem is that the form isn’t dynamic and a little awkward to use.

Basically, they have to fill in the Country, click update shipping. The State, click update shipping, then the postal code, click update shipping. Which finally gives me the result. The other problem with this, is if they choose a country, then update shipping, then want to change back to another country all the old provinces/states are still in there for the old country.

I would like to have this field done dynamically so they can do it all on one click. Fill in all three fields and then click update shipping to get the price.

Usually it would be quite simple with some jQuery, but Woocommerce is using PHP functions to get the allowed countries that we can ship to as defined in the woocommerce settings.

The code for the shipping calculator looks like this

shipping-calculator.php

<?php
/**
 * Shipping Calculator
 *
 * @author      WooThemes
 * @package     WooCommerce/Templates
 * @version     2.0.8
 */

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

global $woocommerce;

if ( get_option( 'woocommerce_enable_shipping_calc' ) === 'no' || ! WC()->cart->needs_shipping() )
    return;
?>

<?php do_action( 'woocommerce_before_shipping_calculator' ); ?>

<form class="shipping_calculator" action="<?php echo esc_url( WC()->cart->get_cart_url() ); ?>" method="post">

    <h4><a href="#" class="shipping-calculator-button"><?php _e( 'Calculate Shipping', 'woocommerce' ); ?></a></h4>

    <section class="shipping-calculator-form">

        <p class="form-row form-row-wide">
            <select name="calc_shipping_country" id="calc_shipping_country" class="country_to_state styled" rel="calc_shipping_state">
                <option value=""><?php _e( 'Select a country&hellip;', 'woocommerce' ); ?></option>
                <?php
                    foreach( WC()->countries->get_shipping_countries() as $key => $value )
                        echo '<option value="' . esc_attr( $key ) . '"' . selected( WC()->customer->get_shipping_country(), esc_attr( $key ), false ) . '>' . esc_html( $value ) . '</option>';
                ?>
            </select>
        </p>

        <p class="form-row form-row-wide" id="state_form_wrapper">
            <?php
                $current_cc = WC()->customer->get_shipping_country();
                $current_r  = WC()->customer->get_shipping_state();
                $states     = WC()->countries->get_states( $current_cc );

                // Hidden Input
                if ( is_array( $states ) && empty( $states ) ) {

                    ?><input type="hidden" name="calc_shipping_state" id="calc_shipping_state" placeholder="<?php _e( 'State / county', 'woocommerce' ); ?>" /><?php

                // Dropdown Input
                } elseif ( is_array( $states ) ) {

                    ?><span>
                        <select name="calc_shipping_state" id="calc_shipping_state" placeholder="<?php _e( 'State / county', 'woocommerce' ); ?>" class="styled">
                            <option value=""><?php _e( 'Select a state&hellip;', 'woocommerce' ); ?></option>
                            <?php
                                foreach ( $states as $ckey => $cvalue )
                                    echo '<option value="' . esc_attr( $ckey ) . '" ' . selected( $current_r, $ckey, false ) . '>' . __( esc_html( $cvalue ), 'woocommerce' ) .'</option>';
                            ?>
                        </select>
                    </span><?php

                // Standard Input
                } else {

                    ?><input type="text" class="input-text" value="<?php echo esc_attr( $current_r ); ?>" placeholder="<?php _e( 'State / county', 'woocommerce' ); ?>" name="calc_shipping_state" id="calc_shipping_state" /><?php

                }
            ?>
        </p>

        <?php if ( apply_filters( 'woocommerce_shipping_calculator_enable_city', false ) ) : ?>

            <p class="form-row form-row-wide">
                <input type="text" class="input-text" value="<?php echo esc_attr( WC()->customer->get_shipping_city() ); ?>" placeholder="<?php _e( 'City', 'woocommerce' ); ?>" name="calc_shipping_city" id="calc_shipping_city" />
            </p>

        <?php endif; ?>

        <?php if ( apply_filters( 'woocommerce_shipping_calculator_enable_postcode', true ) ) : ?>

            <p class="form-row form-row-wide clearfix"  id="postcode_form_wrapper">
                <input type="text" class="input-text" value="<?php echo esc_attr( WC()->customer->get_shipping_postcode() ); ?>" placeholder="<?php _e( 'Postcode / Zip', 'woocommerce' ); ?>" name="calc_shipping_postcode" id="calc_shipping_postcode" />
            </p>
            <p><sup>*</sup> Postcodes are important so that FedEx can accurately calculate shipping, however if your country does not have a postcode please enter the word "none"</p>

        <?php endif; ?>

        <p><button type="submit" name="calc_shipping" id="calc_shipping" value="1" class="button"><?php _e( 'Update Shipping', 'codeus' ); ?></button></p>

        <?php wp_nonce_field( 'woocommerce-cart' ); ?>
    </section>
</form>

<?php do_action( 'woocommerce_after_shipping_calculator' ); ?>

Since it is using PHP functions like:

$current_cc = WC()->customer->get_shipping_country();
$current_r  = WC()->customer->get_shipping_state();
$states     = WC()->countries->get_states( $current_cc );

It seems the only way to accomplish this is with ajax, but I have no idea what I’m doing. How can I make this form dynamic with ajax or jQuery to update without having to click the “Update Shipping” button for every field change?

EDIT: What the shipping calculator form looks like:

Read more here: Woocommerce Shipping Calculator Dynamic Country/State/Postal Code fields

Leave a Reply

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