Skip to content
Advertisement

Change WooCommerce checkout city field to a dropdown for a unique country

In Woocommerce, I wrote some code for billing and shipping city to use the shipping charge. Now I want to change the cities list when change country.

This is my custom code:

// Change "city" checkout billing and shipping fields to a dropdown
add_filter( 'woocommerce_checkout_fields' , 'override_checkout_city_fields' );
function override_checkout_city_fields( $fields ) {
    global $woocommerce;

    // Define here in the array your desired cities (Here an example of cities)
    $option_cities = array(
    "city1"=>"city1",
    "city2"=>"city2",
    "city3"=>"city3"
    );

    $country = $woocommerce->customer->get_shipping_country();
    if ($country == 'SA'){
        $fields['billing']['billing_city']['type'] = 'select';
        $fields['billing']['billing_city']['options'] = $option_cities;
        $fields['shipping']['shipping_city']['type'] = 'select';
        $fields['shipping']['shipping_city']['options'] = $option_cities;

    } else{
        $fields['billing']['billing_city']['type'] = 'text';
        $fields['shipping']['shipping_city']['type'] = 'text';
    }  

    return $fields;
}

But when the user change the country from Saudi Arabia to Qatar, the billing_city does not change to text type, so we must refresh the page to show billing_city as text type.

I need the only billing_city as a select type for only “Saudi Arabia” country. else country we need to show text type.

How can I do that?

Advertisement

Answer

This requires something different with javascript, as it’s based on client side (browser side) event.

So you need to keep the WooCommerce billing city and shipping city fields as they are and Here we will add additional custom select fields (hidden by default).

Then when the selected country will be “Saudi Arabia” (your targeted country), the default WooCommerce city field will be hidden and the related custom select field will be displayed.

When a value will be selected on the city dropdown field, we will copy that value to WooCommerce default city input field (even if it’s hidden).

So when customer will place the order, WooCommerce will have the right city value anyways.

Note that the billing address is different from shipping address and a customer can have a billing country different from the shipping country… So you need to take more care about billing and shipping addresses behaviors.

Here is the complete code:

// Add custom checkout select fields
add_filter( 'woocommerce_checkout_fields', 'add_custom_checkout_select_fields' );
function add_custom_checkout_select_fields( $fields ) {
    // Define HERE in the array, your desired cities
    $cities = array(
        __( 'City1', 'woocommerce' ),
        __( 'City2', 'woocommerce' ),
        __( 'City3', 'woocommerce' ),
    );

    // Format in the right way the options array of cities
    $options = array( '' => __( 'Select a city', 'woocommerce' ) . '…' );
    foreach ( $cities as $city ) {
        $options[$city] = $city;
    }

    // Adding 2 custom select fields
    $fields['billing']['billing_city2'] = $fields['shipping']['shipping_city2'] = array(
        'type'         => 'select',
        'required'     => true,
        'options'      => $options,
        'autocomplete' => 'address-level2',
    );
    
    // Copying data from WooCommerce city fields
    $fields['billing']['billing_city2']['class'] = array_merge($fields['billing']['billing_city']['class'], array('hidden') );
    $fields['shipping']['shipping_city2']['class'] = array_merge($fields['shipping']['shipping_city']['class'], array('hidden') );
    $fields['billing']['billing_city2']['label'] = $fields['billing']['billing_city']['label'];
    $fields['shipping']['shipping_city2']['label'] = $fields['shipping']['shipping_city']['label'];
    $fields['billing']['billing_city2']['priority'] = $fields['billing']['billing_city']['priority'] + 5;
    $fields['shipping']['shipping_city2']['priority'] = $fields['shipping']['shipping_city']['priority'] + 5;

    return $fields;
}

// Custom inline styles to hide some checkout fields
add_action( 'wp_head', 'custom_checkout_css' );
function custom_checkout_css() {
    // Only checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ) {
        ?><style>
        #billing_city_field.hidden, #billing_city2_field.hidden,
        #shipping_city_field.hidden, #shipping_city2_field.hidden
        {display:none !important;}
        </style><?php
    }
}

// Custom jQuery code
add_action( 'wp_footer', 'custom_checkout_js_script' );
function custom_checkout_js_script() {
    // Only checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ):
    ?>
    <script type="text/javascript">
    (function($){
        var targetedCountry = 'FR',
            initialBCountry = '<?php echo WC()->customer->get_billing_country(); ?>',
            initialSCountry = '<?php echo WC()->customer->get_shipping_country(); ?>';

        function showHideFields( country, fieldset ) {
            var select2Classes = 'country_select select2-hidden-accessible';

            if( country === targetedCountry ) {
                $('#'+fieldset+'_city2_field').removeClass('hidden');
                $('#'+fieldset+'_city_field').addClass('hidden');
                $('select#'+fieldset+'_city2').addClass(select2Classes);
            } else if( country !== targetedCountry && $('#'+fieldset+'_city_field').hasClass('hidden') ) {
                $('#'+fieldset+'_city2_field').addClass('hidden');
                $('#'+fieldset+'_city_field').removeClass('hidden');
                $('select#'+fieldset+'_city2').removeClass(select2Classes);
            }
        }

        // 1. On Start (after Checkout is loaded)
        showHideFields(initialBCountry, 'billing');
        showHideFields(initialSCountry, 'shipping');

        // 2. Live: On Country change event
        $('body').on( 'change', 'select#billing_country', function(){
            showHideFields($(this).val(), 'billing');
        });
        $('body').on( 'change', 'select#shipping_country', function(){
            showHideFields($(this).val(), 'shipping');
        });

        // 3. Live: On City change event for "Saudi Arabia" country
        $('body').on( 'change', 'select#billing_city2', function(){
             $('input#billing_city').val($(this).val());
        });
        $('body').on( 'change', 'select#shipping_city2', function(){
             $('input#shipping_city').val($(this).val());
        });
    })(jQuery);
    </script>
    <?php
    endif;
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement