Get a free advice now!

    Pick the topic

    [checkbox* "Developer Outsourcing" "Web developing" "App developing" "Digital Marketing" "eCommerce systems" "eEntertainment systems"]

    Thank you for your message. It has been sent.

    Tags

    Shop listing variable Product add to cart

    Shop listing variable Product add to cart

    CHALLENGE: allow WooCommerce customers to choose variations directly on product listing

    SOLUTION: overwrite the content-product.php template and add custom javascript

    The WooCommerce shop listing presents a list of products. Usually, normal products include the ‘Add to cart’ button, but products with the “variable” type only have the ‘Read more’ button: you need to go to the single product view, and there you have the ability to choose the proper variations and add an item to the cart.

    It would be great to have a nice and user friendly feature allowing the Customer to pick sizes / colors directly on product listing. Instead of navigating to a single product page, the user can shop using the main listing view.

    Custom Add to cart

    We’re going to add the ability to pick variations directly on shop listing. One WooCommerce template  – content-product.php will be overwritten in the theme.

    <?php
    /**
     * wp-content/themes/mytheme/woocommerce/content-product.php
     * Code by John Enrick
     */
    defined( 'ABSPATH' ) || exit;
    global $product;
    $variations = [];
    if(get_class($product ) != 'WC_Product_Simple'){
       $variations = $product->get_variation_attributes();
    }
    $link = apply_filters( 'woocommerce_loop_product_link', get_the_permalink(), $product );
    // Ensure visibility.
    if ( empty( $product ) || ! $product->is_visible() ) {
       return;
    }
    ?>
    <li <?php wc_product_class( '', $product ); ?>>
       <?php
       /**
        * Hook: woocommerce_before_shop_loop_item.
        *
        * @hooked woocommerce_template_loop_product_link_open - 10
        */
       if(count($variations)){
          do_action( 'woocommerce_before_shop_loop_item' );
       }
       
       
       /**
        * Hook: woocommerce_before_shop_loop_item_title.
        *
        * @hooked woocommerce_show_product_loop_sale_flash - 10
        * @hooked woocommerce_template_loop_product_thumbnail - 10
        */
       woocommerce_template_loop_product_link_open();
       
       do_action( 'woocommerce_before_shop_loop_item_title' );
       /**
        * Hook: woocommerce_shop_loop_item_title.
        *
        * @hooked woocommerce_template_loop_product_title - 10
        */
       do_action( 'woocommerce_shop_loop_item_title' );
       /**
        * Hook: woocommerce_after_shop_loop_item_title.
        *
        * @hooked woocommerce_template_loop_rating - 5
        * @hooked woocommerce_template_loop_price - 10
        */
       do_action( 'woocommerce_after_shop_loop_item_title' );
       woocommerce_template_loop_product_link_close();
       if(count($variations)){
          remove_action('woocommerce_after_shop_loop_item', 'woocommerce_template_loop_product_link_close');
          do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' );
       }
       do_action( 'woocommerce_after_shop_loop_item' );
       /**
        * Hook: woocommerce_after_shop_loop_item.
        *
        * @hooked woocommerce_template_loop_product_link_close - 5
        * @hooked woocommerce_template_loop_add_to_cart - 10
        */
       
       
       
       ?>
    </li>

    Listen to variation changes

    Typically, product variations can have multiple parameters, like: sizes, colors etc. Each variation also includes a separate price and an individual stock amount. It’s important to reload those values dynamically and display proper values to the Customer.

    The function wp_localize_script will help with passing translations to the javascript file. We’re using built-in WooCommerce phrases that are already translated. So, the function will work nicely in all languages that WooCommerce supports. Translations handle both possibilities: adding 1 or multiple products to the cart: ‘%s has been added to your cart.’ vs ‘%s have been added to your cart.’

    Add to cart for “variable product” uses AJAX reload, so the page will not be refreshed. To enhance user experience, we’re going to add an additional sweetalert modal that will confirm the product has been added to the cart.

    Enqueuing assets and definitions for text translations are added in functions.php :

    /**
     * functions.php
     */
    add_action( 'wp_enqueue_scripts', 'ct_scripts', 10 );
    function ct_scripts() {
        $storefront_version = 5;
        if(is_woocommerce() && !is_product()){
            wp_enqueue_script('sweetalert2-js', get_template_directory_uri() . '/assets/js/sweetalert2.all.min.js', array(), $storefront_version, true );
            wp_enqueue_script('product-loop', get_template_directory_uri() . '/woocommerce/assets/js/frontend/product-loop.js', array(), $storefront_version, true );
            wp_localize_script('product-loop', 'cartSettings', array(
                'cart_url' => wc_get_cart_url(),
                'item_added_message' => _n( '%s has been added to your cart.', '%s have been added to your cart.', 1, 'woocommerce' ),
                'item_added_message2' => _n( '%s has been added to your cart.', '%s have been added to your cart.', 2, 'woocommerce' ),
                'add_to_cart' => __( 'Add to cart', 'woocommerce' ),
                'view_cart' => __( 'View cart', 'woocommerce' ),
                'select' => __( 'Select options', 'woocommerce' )
            ));
        }
    }

    Ajax Add to cart

    Custom JS will handle ajax add to cart, displaying a confirmation message, an alert popup and hiding unused item elements:

    // wp-content/themes/mytheme/woocommerce/assets/js/frontend/product-loop.js
    // Code by John Enrick
    (function($){
        function addItemAddedMessage(productName, addedQuantity){
            var msg =  cartSettings.item_added_message.replace("%s", productName);
            if(addedQuantity>1){
                msg =  addedQuantity + ' x ' + cartSettings.item_added_message2.replace("%s", productName);
            }
            var viewCart = cartSettings.view_cart;
            var cartUrl = cartSettings.cart_url;
            $('.container > .woocommerce').html(`
                <div class="woocommerce-message" role="alert">
                  <a href="${cartUrl}" class="button wc-forward">${viewCart}</a>
                  ${msg}
                </div>
            `)
            $('.item--menu-cart .cart-basket').text(($('.item--menu-cart .cart-basket').text() * 1) + addedQuantity)
            Swal.fire({
                position: 'top-end',
                icon: 'success',
                title: msg,
                showConfirmButton: false,
                timer: 1500
            })
        }
        function submitVariableAddToCart(event){
            var form = $(this)
            form.find('.single_add_to_cart_button').css('min-width', getComputedStyle(form.find('.single_add_to_cart_button')[0]).width)
            form.find('.single_add_to_cart_button').attr('disabled', true)
            form.find('.single_add_to_cart_button').addClass("loading");
            event.preventDefault()
            var formDataArray = form.serializeArray()
            var formData = {}
            formDataArray.forEach(function(input){
                formData[input['name']] = input['value']
            })
            $.ajax({
                type: 'POST',
                url: form.attr('action'),
                data: formData,
                success: function(response)
                {
                    addItemAddedMessage(form.parent().find('.woocommerce-loop-product__title').text(), form.parent().find('.quantity input').val() * 1)
                    form.find('.single_add_to_cart_button').removeClass("loading");
                    setTimeout(function(){
                        form.find('.single_add_to_cart_button').attr('disabled', false)
                        form.find('.single_add_to_cart_button').text(cartSettings.add_to_cart)
                    }, 100)
                },
                error: function(response)
                {
                    form.find('.single_add_to_cart_button').attr('disabled', false)
                    form.find('.single_add_to_cart_button').text(cartSettings.add_to_cart)
                }
            })
        }
        /**
         * Hide the Variable Add to Cart button if no variation is selected yet
         */
        function hideVariableAddToCartButtons(){
            $('ul.products > li.product.type-product .variations_form .single_variation_wrap').hide()
        }
        function changeVariationSelectDefaultText(){
            $('ul.products > li.product.type-product .variations select').each(function(){
                var label = $(this).parents('tr').find('label').text()
                label = label.toLowerCase().replace(/\b[a-z]/g, function(letter) {
                    return letter.toUpperCase();
                });
                $(this).find('option:first-child').text(cartSettings.select +' ' + label)
            })
        }
        function listenVariationChange(){
            $('ul.products').on('change', '.variations select', function(){
                var tableElement = $(this).parents('table.variations')
                var hasSelectWithValue = false;
                tableElement.find('select').each(function(){
                    if($(this).val() !== '' && $(this).val() != '0'){
                        hasSelectWithValue = true
                        return false
                    }
                })
                var cardContainer = tableElement.parents('li.product')
                if(hasSelectWithValue){
                    cardContainer.find('.single_variation_wrap').show()
                    cardContainer.find('a.product_type_variable').hide()
                }else{
                    cardContainer.find('.single_variation_wrap').hide()
                    cardContainer.find('a.product_type_variable').show()
                }
            })
        }
        $(document).ready(function(){
            $('#content').on('submit', 'form.variations_form', submitVariableAddToCart)
            listenVariationChange()
            setTimeout(function(){
                hideVariableAddToCartButtons()
                changeVariationSelectDefaultText()    
            }, 100)
            
        })
    })(jQuery)

    Product loop styling

    The final step will be to add some styling. SCSS Sass style will adjust the position of elements and apply some basic look.

    /**
    wp-content/themes/mytheme/assets/scss/woocommerce/_product_loop.scss
     */
    @mixin p-1(){
        padding: 0.5em!important;
    }
    ul.products li.product.type-product{
        .variations_form table.variations {
            width: 100%;
            select {
                margin-bottom:0.5rem;
            }
            td:first-child {
                display: none
            }
            td:last-child {
                width: 100%
            }
        }
        .woocommerce-variation-add-to-cart {
            display: flex;
            justify-content: flex-end;
            div.quantity {
                flex-grow: 1;
                padding-right: 4px;
                input {
                    text-align: right;
                    @include p-1();
                    width: 100%;
                }
            }
            
            button {
                @include p-1();
                font-size: 1em!important;
                text-transform: capitalize;
                white-space: nowrap;
            }
        }
        
    }
    ul.products li.product.type-product.product-type-simple{
        form.cart {
            display: flex;
            justify-content: flex-end;
            div.quantity {
                flex-grow: 1;
                padding-right: 4px;
                input {
                    text-align: right;
                    @include p-1();
                    width: 100%;
                }
            }
            
            button {
                @include p-1();
                font-size: 1em!important;
                text-transform: capitalize;
                white-space: nowrap;
            }
        }
    }
    Three columns with space for an image in the header and product data below
    A notification in the upper right corner with a product listing in the background

    That’s it for today’s tutorial. Be sure to follow us for other useful tips and guidelines.

    Comments
    0 response

    Add comment

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

    Popular news

    PHPStorm – fix long load time of a directory window
    • Dev Tips and Tricks

    PHPStorm – fix long load time of a directory window

    January 20, 2023 by createIT
    reCAPTCHA v3 – WordPress implementation
    • Dev Tips and Tricks

    reCAPTCHA v3 – WordPress implementation

    January 20, 2023 by createIT
    How to compare GIT and server files
    • Dev Tips and Tricks

    How to compare GIT and server files

    January 19, 2023 by createIT
    How to trigger a click event inside iframe?
    • Dev Tips and Tricks

    How to trigger a click event inside iframe?

    January 19, 2023 by createIT
    FOOEvents – generate a custom CSV report
    • Dev Tips and Tricks

    FOOEvents – generate a custom CSV report

    January 19, 2023 by createIT
    Headless chrome – testing webgl using playwright
    • Dev Tips and Tricks

    Headless chrome – testing webgl using playwright

    January 18, 2023 by createIT
    Preview big SQL files with PilotEdit
    • Dev Tips and Tricks

    Preview big SQL files with PilotEdit

    January 18, 2023 by createIT
    Outsourcing a team of backend developers
    • Our Highlights

    Outsourcing a team of backend developers

    January 18, 2023 by createIT
    A new look for an aspiring brand
    • Our Highlights

    A new look for an aspiring brand

    January 18, 2023 by createIT
    Percy AI visual regressions – playwright example
    • Dev Tips and Tricks

    Percy AI visual regressions – playwright example

    January 17, 2023 by createIT

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us