WooCommerce checkout postcode field order

I’m trying to modify the checkout page. Using a plugin I was able to replace the two default address lines with a street, house number and ‘extra’ field (they’re all on one line). Below that, you find the postcode and the city field.
However, I want to generate the street and city when the user enters their postcode and housenumber, so I want to switch the fields.
It’s like this:

Street – Housenumber – Extra
Postcode – City

Read More

I want it to be:

Postcode – Housnumber – Extra
Street – City

I’m using the regular hook:

add_filter("woocommerce_checkout_fields", "order_fields");

function order_fields($fields) {

    $order = array(
        "billing_first_name", 
        "billing_last_name", 
        "billing_company", 
        "billing_street",                       
        "billing_house_number",
        "billing_house_number_extra",
        "billing_postcode",
        "billing_city",
        "billing_country", 
        "billing_email", 
        "billing_phone"

    );
    foreach($order as $field)
    {
        $ordered_fields[$field] = $fields["billing"][$field];
    }

    $fields["billing"] = $ordered_fields;
    return $fields;

}

However… trying to move the postcode field is unsuccesful. If I move the postcode and city field together I’m able to move them, but just trying to move the postcode field seperate shows strange behaviour (For instance: switching out the street and postcode field).

What am I missing?

Related posts

Leave a Reply

2 comments

  1. As @XciD said in his answer, the issue isn’t with the actual field ordering, as your woocommerce_checkout_fields hook to re-order them is fine. The problem is with WooCommerce’s enqueued address-i18n.js script, which checks your store’s locale and re-orders the checkout (and account address) fields on the fly based on that (which is pretty annoying, after ostensibly giving you a hook to re-order them in the first place…).

    Caveat

    Unfortunately, there’s no hugely straightforward way around this; I ended up coming up with a fairly hacky inline jQuery shiv to move the fields back into the order I originally set; however it’s worth noting that the WooCommerce team may be completely refactoring all of this with the 2.7 release, so I don’t know how long this will be relevant for.

    With that said: on with the hack!

    function vnmTheme_addressFieldsOverride() {
        if (is_wc_endpoint_url('edit-address') || is_checkout()) {
            ?>
    
            <script>
                jQuery(document).ready(function($) {
    
                    $(document.body).on('country_to_state_changing', function(event, country, wrapper) {
    
                        var $postcodeField = wrapper.find('#billing_postcode_field, #shipping_postcode_field');
                        var $housenoField = wrapper.find('#billing_house_number_field, #shipping_house_number_field' );
    
                        var fieldTimeout = setTimeout(function() {
                            $postcodeField.insertBefore($housenoField);
                        }, 50);
                    });
    
                });
            </script>
    
            <?php
        }
    }
    
    add_action('wp_footer', 'vnmTheme_addressFieldsOverride', 999);
    

    Using wp_footer (and assuming we only want to run this on the checkout or the ‘Edit Address’ pages under My Account), this inserts some inline jQuery that listens for the same event as in address-18n.js ('country_to_state_changing'). When that event is triggered, it waits for 50ms before reshuffling the fields and placing postcode before your custom house_number field. The delay is because both listeners will get triggered at the same time, and it appears that the address-i18n.js takes priority, so setTimeout ensures that our custom one does its work a little later.

    I used this solution with the regular state field and it worked just fine. Make sure to check your house_number field ID to make sure the selector is correct, but this should solve your issue too hopefully.

    Oh! And it’s also worth noting that this will only work if you’re not deferring the jQuery load, but that’s the case with all inline jQuery, including all the stuff WooCommerce inserts into the checkout page.

  2. It will be difficult. It’s because of the class of the input.

    First you have to do something like that :

    add_filter("woocommerce_checkout_fields", "order_fields");
    
    function order_fields($fields) {
    
        $order = array(
            "billing_first_name" => [],
            "billing_last_name" => [],
            "billing_company" => [],
            "billing_street" => [],
            "billing_postcode" => ["custom-css"],
            "billing_house_number" => [],
            "billing_house_number_extra" => [],
            "billing_city" => [],
            "billing_country" => [],
            "billing_email" => [],
            "billing_phone" => []
        );
    
        foreach($order as $key => $class)
        {
            $field = $fields["billing"][$key];
    
            if(sizeof($class) > 0){
                $field['class'] = $class;
            }
    
            $ordered_fields[$key] = $field;
        }
    
        $fields["billing"] = $ordered_fields;
        return $fields;
    }
    

    Then you have to find a way to override WooCommerce address-i18n js or write your own js.

    https://github.com/woothemes/woocommerce/blob/2.6.1/assets/js/frontend/address-i18n.js#L47

    The problem is the form-row-first form-row-last class adding some float into the inputs