2

We just moved and merged the WooCommerce account password field on the checkout page from the "Account" group to the "Billing" field group. This works nicely. Also, we create a new checkbox field and add this to the billing section. Without that, the "Create an account" checkbox will still be at the default position. To do so, we created a new checkbox with jQuery.

Thanks to the following articles:

With our current code, the moved password field is always visible. We want to have it the same way as WooCommerce default works. This means the password field should only be visible when the checkbox is active. WooCommerce by default has an excellent hide & Show CSS in place. However, I'm not able to use the identical CSS (or maybe script?!).

How can we connect the custom checkbox with the account password field?

// Move Account password field into billing section
function move_password_field($checkout_fields){
    // Move Account Password into Billing
    $checkout_fields['billing']['account_password'] = $checkout_fields['account']['account_password']; 

    // Remove Password from Billing
    unset($checkout_fields['account']['account_password']);

    return $checkout_fields;
}

add_filter('woocommerce_checkout_fields', 'move_password_field', 999); 

// CSS rules
add_action( 'woocommerce_before_checkout_billing_form', 'move_checkout_create_an_account_css' );
function move_checkout_create_an_account_css() {
    if( ! is_user_logged_in() ) :
    ?><style>
        .woocommerce-account-fields label.woocommerce-form__label-for-checkbox {display: none !important;}
        #account_cb_field {margin-top: 32px;}
        #account_cb_field input {margin-right: 6px;}
    </style>
    <?php
    endif;
}

// Add a checkbox to billing section
add_filter( 'woocommerce_checkout_fields', 'move_checkout_create_an_account_checkbox' );
function move_checkout_create_an_account_checkbox( $fields ) {
    if( ! is_user_logged_in() ) {
        // Make email field on half on left side
        $fields['billing']['billing_email']['class'] = array('form-row-wide');

        // Custom checkbox on half right side
        $fields['billing']['account_cb'] = array(
            'type'  => 'checkbox',
            'label' => __("Create an account?", "woocommerce"),
            'class' => array('form-row-wide'),
        );
    }
    return $fields;
}

// remove "(optional)" from the new checkbox
add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page
    if ( is_checkout() && ! is_wc_endpoint_url() && $key === 'account_cb' ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field = str_replace( $optional, '', $field );
    }
    return $field;
}

// The jQuery code
add_action( 'wp_footer', 'move_checkout_create_an_account_js' );
function move_checkout_create_an_account_js() {
    if ( ! is_user_logged_in() && is_checkout() && ! is_wc_endpoint_url() ) :
    ?><script>
    (function($){
        $('input[name=account_cb]').on( 'click', function() {
            $('input[name=createaccount]').trigger('click');
        });
    })(jQuery);
    </script>
    <?php
    endif;
}
Nik7
  • 346
  • 2
  • 16

1 Answers1

3

there are a fair few issues with that code namely Warning: Undefined array key "account_password" in /var/www/html/wp-content/themes/storefront/functions.php

but here is the solution to your initial problem

// The jQuery code
add_action( 'wp_footer', 'move_checkout_create_an_account_js' );
function move_checkout_create_an_account_js() {
    if ( ! is_user_logged_in() && is_checkout() && ! is_wc_endpoint_url() ) :
        ?>
    <script>
    (function($) {
        // re-usable function to play around with the checkout fields if needed in the
        // future
        const toggleElmVisibility = (selector = '', show = true, duration = 200) => {
            if (show) {
                $(selector).show(duration);
                return;
            }
            $(selector).hide(duration);
        }

        // hide password fields on page load, unless they have alredy checked the checkbox
        toggleElmVisibility('#account_password_field', $('#account_cb').is(':checked'));

        $('#account_cb').on('change', function() {
            toggleElmVisibility('#account_password_field', $(this).is(':checked'));
        });



        $('input[name=account_cb]').on('click', function() {
            $('input[name=createaccount]').trigger('click');
        });
    })(jQuery);
    </script>
        <?php
    endif;
}

Edit:

Changes to function to add the password field

// Move Account password field into billing section
function move_password_field( $checkout_fields ) {
    //bail if not logged in
    if ( is_user_logged_in() ) {
        return $checkout_fields;
    }
    // Move Account Password into Billing if not logged in
    $checkout_fields['billing']['account_password'] = $checkout_fields['account']['account_password'];
    $checkout_fields['billing']['account_password'] = array(
        'label'       => __( 'Password', 'woocommerce' ), // Add custom field label
        'placeholder' => _x( 'Password', 'placeholder', 'woocommerce' ), // Add custom field placeholder
        'clear'       => false, // add clear or not
        'type'        => 'text', // add field type
    );
    unset( $checkout_fields['account']['account_password'] );
    return $checkout_fields;
} add_filter( 'woocommerce_checkout_fields', 'move_password_field', 999 );

change the remove optional text function to catch the new field

// remove "(optional)" from the new checkbox
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page
    if ( is_checkout() && ! is_wc_endpoint_url() && ( $key === 'account_cb' || 'account_password' === $key ) ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field    = str_replace( $optional, '', $field );
    }
    return $field;
} add_filter( 'woocommerce_form_field', 'remove_checkout_optional_fields_label', 10, 4 );

submit validation

/**
 * Validate the password field if checkbox is checked on checkout submit
 *
 * @return void
 * @see https://docs.woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/
 */
function woocomerce_validate_pass(): void {
    if ( isset( $_POST['account_cb'] ) && empty( $_POST['account_password'] ) ) {
        wc_add_notice( __( 'Please enter a password.', 'woocommerce' ), 'error' );
    }
} add_action( 'woocommerce_after_checkout_validation', 'woocomerce_validate_pass' );

and changes to js to pickup that the field is required

    (function($) {
        // re-usable function to play around with the checkout fields if needed in the
        const toggleElmVisibility = (selector = '', show = true, duration = 200) => {
            if (show) {
                $(selector).show(duration, () => {
                    $(selector).addClass("validate-required");
                });
            } else {
                $(selector).hide(duration, () => {
                    $(selector).removeClass("validate-required");
                });
            }
            $(selector).removeClass("woocommerce-validated");
            $(selector).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
        }

        // hide password fields on page load, unless they have alredy checked the checkbox
        toggleElmVisibility('#account_password_field', $('#account_cb').is(':checked'));

        $('#account_cb').on('change', function() {
            toggleElmVisibility('#account_password_field', $(this).is(':checked'));
        });

        $('input[name=account_cb]').on('click', function() {
            $('input[name=createaccount]').trigger('click');
        });
    })(jQuery);

the other functions have been removed, the complete code:


// Move Account password field into billing section
function move_password_field( $checkout_fields ) {
    //bail if not logged in
    if ( is_user_logged_in() ) {
        return $checkout_fields;
    }
    // Move Account Password into Billing if not logged in
    $checkout_fields['billing']['account_password'] = $checkout_fields['account']['account_password'];
    $checkout_fields['billing']['account_password'] = array(
        'label'       => __( 'Password', 'woocommerce' ), // Add custom field label
        'placeholder' => _x( 'Password', 'placeholder', 'woocommerce' ), // Add custom field placeholder
        'clear'       => false, // add clear or not
        'type'        => 'text', // add field type
    );
    unset( $checkout_fields['account']['account_password'] );
    return $checkout_fields;
} add_filter( 'woocommerce_checkout_fields', 'move_password_field', 999 );

// Add a checkbox to billing section
function move_checkout_create_an_account_checkbox( $fields ) {
    if ( is_user_logged_in() ) {
        return $fields;
    }
    // Make email field on half on left side
    $fields['billing']['billing_email']['class'] = array( 'form-row-wide' );
    // Custom checkbox on half right side
    $fields['billing']['account_cb'] = array(
        'type'        => 'checkbox',
        'label'       => __( 'Create an account?', 'woocommerce' ),
        'placeholder' => _x( 'Create an account?', 'placeholder', 'woocommerce' ),
        'class'       => array( 'form-row-wide' ),
    );
    return $fields;
} add_filter( 'woocommerce_checkout_fields', 'move_checkout_create_an_account_checkbox' );


// remove "(optional)" from the new checkbox
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page
    if ( is_checkout() && ! is_wc_endpoint_url() && ( $key === 'account_cb' || 'account_password' === $key ) ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field    = str_replace( $optional, '', $field );
    }
    return $field;
} add_filter( 'woocommerce_form_field', 'remove_checkout_optional_fields_label', 10, 4 );

/**
 * Validate the password field if checkbox is checked on checkout submit
 *
 * @return void
 * @see https://docs.woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/
 */
function woocomerce_validate_pass($fields, $errors): void {
    if ( isset( $_POST['account_cb'] ) && empty( $_POST['account_password'] ) ) {
$errors->add( 'validation', 'Please enter a password.');
    //  wc_add_notice( __( 'Please enter a password.', 'woocommerce' ), 'error' );
    }
} add_action( 'woocommerce_after_checkout_validation', 'woocomerce_validate_pass', 10, 2 );

// The jQuery code
add_action( 'wp_footer', 'move_checkout_create_an_account_js' );
function move_checkout_create_an_account_js() {
    if ( ! is_user_logged_in() && is_checkout() && ! is_wc_endpoint_url() ) :
        ?>
    <script>
    (function($) {
        // re-usable function to play around with the checkout fields if needed in the
        const toggleElmVisibility = (selector = '', show = true, duration = 200) => {
            if (show) {
                $(selector).show(duration, () => {
                    $(selector).addClass("validate-required");
                });
            } else {
                $(selector).hide(duration, () => {
                    $(selector).removeClass("validate-required");
                });
            }
            $(selector).removeClass("woocommerce-validated");
            $(selector).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
        }

        // hide password fields on page load, unless they have alredy checked the checkbox
        toggleElmVisibility('#account_password_field', $('#account_cb').is(':checked'));

        $('#account_cb').on('change', function() {
            toggleElmVisibility('#account_password_field', $(this).is(':checked'));
        });

        $('input[name=account_cb]').on('click', function() {
            $('input[name=createaccount]').trigger('click');
        });
    })(jQuery);
    </script>
        <?php
    endif;
}
  • Working perfect! BUT there is one error still. When a customer is logged in already, the password field is visible. So I think something is not 100% right with my code. Do you have a idea why? The account password box is actually only for "! is_user_logged_in()" users – Nik7 Feb 22 '23 at 20:23
  • 1
    I have updated my answer with a couple of quick fixes to get everything working as expected, switching the `! is_user_logged_in()` to a guard clause to bail if they are logged in The jquery should be moved to an external file and included via enque scripts with the same 'bail if logged in' clause – HamishFleming Feb 23 '23 at 03:49
  • Very smart way! Thanks. Its works now but only one little issue is still there. There is no validation on the password field now. So a customer can finish the process (when the Create checkbox is selected) without entering a password. Do we have to do here a custom validation? – Nik7 Feb 23 '23 at 12:15
  • 1
    Apologies I keep seeing this after I'm mentally clocked out, yes we will need to do some custom validation in the `woocommerce_after_checkout_validation` hook, I edited the function (from my phone mind you so try at your own risk); but you can add any additional validation you would like in that function, setting an error if it fails – HamishFleming Feb 23 '23 at 13:05
  • Thanks you. But I'm not able to get that to work. Also with woocommerce_after_checkout_validation is always not validating the password field. :( – Nik7 Feb 24 '23 at 10:10
  • I still facing issues with validation. Dont have much experience with that topic. Can you help me of how I can do that in order that we can use the code? :) – Nik7 Feb 27 '23 at 12:58
  • Couldn't you just add the `required` attribute via javascript to the password input field after the fact? https://stackoverflow.com/questions/18770369/how-to-set-html5-required-attribute-in-javascript – gurtner Feb 28 '23 at 18:54