WooCommerce

Remove Product in the Cart Widget Using AJAX

Remove Product in the Cart Widget Using AJAX

WooCommerce – one and the best extensions for building an online store with which I have worked, but like other extensions it is not ideal. In my projects, I tried to make it better and add useful features to make it even more user-friendly. In this article, we’ll talk about how to remove product in the shopping cart widget using AJAX.

You must change the mini-cart.php template and add the attribute data-cart_item_key to the deletion link


<?php echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
    '<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s" data-cart_item_key="%s">×</a>',
    esc_url( WC()->cart->get_remove_url( $cart_item_key ) ),
    esc_html__( 'Remove this item', 'deepsoul' ),
    esc_attr( $product_id ),
    esc_attr( $_product->get_sku() ),
    esc_attr( $cart_item_key )
), $cart_item_key ); ?>
If this is not done there will be problems when deleting variable products

Next, we need to add a click event to the product deletion link in the cart. This code should be added to the main JS file of your theme.


// Ajax delete product in the cart
$(document).on('click', '.mini_cart_item a.remove', function (e)
{
    e.preventDefault();

    var product_id = $(this).attr("data-product_id"),
        cart_item_key = $(this).attr("data-cart_item_key"),
        product_container = $(this).parents('.mini_cart_item');

    // Add loader
    product_container.block({
        message: null,
        overlayCSS: {
            cursor: 'none'
        }
    });

    $.ajax({
        type: 'POST',
        dataType: 'json',
        url: wc_add_to_cart_params.ajax_url,
        data: {
            action: "product_remove",
            product_id: product_id,
            cart_item_key: cart_item_key
        },
        success: function(response) {
            if ( ! response || response.error )
                return;

            var fragments = response.fragments;

            // Replace fragments
            if ( fragments ) {
                $.each( fragments, function( key, value ) {
                    $( key ).replaceWith( value );
                });
            }
        }
    });
});

Add action to functions.php file to handle AJAX request


// Remove product in the cart using ajax
function warp_ajax_product_remove()
{
    // Get mini cart
    ob_start();

    foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item)
    {
        if($cart_item['product_id'] == $_POST['product_id'] && $cart_item_key == $_POST['cart_item_key'] )
        {
            WC()->cart->remove_cart_item($cart_item_key);
        }
    }

    WC()->cart->calculate_totals();
    WC()->cart->maybe_set_cart_cookies();

    woocommerce_mini_cart();

    $mini_cart = ob_get_clean();

    // Fragments and mini cart are returned
    $data = array(
        'fragments' => apply_filters( 'woocommerce_add_to_cart_fragments', array(
                'div.widget_shopping_cart_content' => '<div class="widget_shopping_cart_content">' . $mini_cart . '</div>'
            )
        ),
        'cart_hash' => apply_filters( 'woocommerce_add_to_cart_hash', WC()->cart->get_cart_for_session() ? md5( json_encode( WC()->cart->get_cart_for_session() ) ) : '', WC()->cart->get_cart_for_session() )
    );

    wp_send_json( $data );

    die();
}

add_action( 'wp_ajax_product_remove', 'warp_ajax_product_remove' );
add_action( 'wp_ajax_nopriv_product_remove', 'warp_ajax_product_remove' );

Do you want to see how it works?

Demo

Comments (21)

  • Avatar

    Gantsta

    |

    Thanks so much for this. I’ve spent all morning trying to write this functionality. Your code has proved invaluable!

    Reply

    • Avatar

      Alex Podolyan

      |

      I am glad to help 🙂

      Reply

  • Avatar

    Denis

    |

    Alex Thank you!

    Reply

  • Avatar

    mauricio

    |

    good

    Reply

  • Avatar

    Colaps

    |

    Thanks you very much for this but I have one question, from the cart page when I remove a product from de widget the product is removed from the widget but not from the cart product list and if I remove a product from the cart product list it remove the product from the widget.
    Is it possible to have both ways ?
    Hope you understand, thanks you again!)

    Reply

    • Avatar

      Alex Podolyan

      |

      Thank you for your question.
      Yes, there is an easy way to do this. You need to add one line of code to the Ajax request.

      $( document.body ).trigger( 'wc_update_cart' );

      It should be something like this

      
      // Ajax delete product in the cart
      $(document).on('click', '.mini_cart_item a.remove', function (e)
      {
          e.preventDefault();
      
          var product_id = $(this).attr("data-product_id"),
              cart_item_key = $(this).attr("data-cart_item_key"),
              product_container = $(this).parents('.mini_cart_item');
      
          // Add loader
          product_container.block({
              message: null,
              overlayCSS: {
                  cursor: 'none'
              }
          });
      
          $.ajax({
              type: 'POST',
              dataType: 'json',
              url: wc_add_to_cart_params.ajax_url,
              data: {
                  action: "product_remove",
                  product_id: product_id,
                  cart_item_key: cart_item_key
              },
              success: function(response) {
                  if ( ! response || response.error )
                      return;
      
                  var fragments = response.fragments;
      
                  // Replace fragments
                  if ( fragments ) {
                      $.each( fragments, function( key, value ) {
                          $( key ).replaceWith( value );
                      });
                  }
             
                  // Update cart
                  $( document.body ).trigger( 'wc_update_cart' );
              }
          });
      });
      

      Reply

  • Avatar

    pradsdev

    |

    I’ve tried pasting the code to my woocmmerce site. I’m facing a problem with this. Basically it did removed the item from mini cart and the cart. After that i can’t be see items in mini cart. But products are there in the cart. and mini cart tray is getting closed after removing the item. Can you help me to resolve this?

    Reply

    • Avatar

      Alex Podolyan

      |

      Hi,

      Can you send me a link to the site or test page and FTP access on the my e-mail elartica.hub@gmail.com
      I’ll try to help you tomorrow.

      Best, Alex

      Reply

      • Avatar

        Romel

        |

        Hi Alex, is pradsdev’s concern has solution? I have the same issue. Btw I like your work here. Its awesome!

        Reply

        • Avatar

          Alex Podolyan

          |

          Hello,

          No, he don’t wrote to me on the email, apparently he was able to solve the problem himself. But if you need help you can send me a link to the site or test page and FTP access on the my e-mail. I’ll try to help you.

          Best, Alex

          Reply

  • Avatar

    unknown

    |

    Getting this error on WC ver 3.4.3. Block is not a function. Please help.

    $(document).on("click", ".header-cart-item-img", function() {
        let imageBlock = $(this);
    
        $.ajax({
            url: `${cozastore.siteurl}?wc-ajax=remove_from_cart`,
            type: "post",
            data: {
                cart_item_key: imageBlock.data("cartitemkey")
            },
            dataType: "json",
            beforeSend: function () {
                imageBlock.closest("li").block({
                    message: null,
                    overlayCSS: {
                        cursor: 'none'
                    }
                });
            },
            success: function(resp) {
                if ( ! resp || resp.error )
                    return;
    
                if( resp && resp.fragments ) {
                    $.each( resp.fragments, function( key, value ) {
                        $( key ).replaceWith( value );
                    });
                }
            }
        })
    });
    

    Reply

    • Avatar

      Alex Podolyan

      |

      I think the problem is that this function inside the AJAX request is not available. You need to use it before the AJAX request and not inside using ‘beforeSend’

      Reply

  • Avatar

    Fabian

    |

    Thanks for this code. I would like to make some modifications, but I am a total beginner in JS and AJAX. I added two functions to fade in/out the subtotal amount. Fade out is working fine, because I am calling it right after the click function. But I don’t know where I should put the fade in function. Can you help me with that?

    My functions:
    ` $.fade_out_subtotal = function(){

    $(‘.sidecart-subtotal-amount’).fadeOut(1000);
    }
    $.fade_in_subtotal = function(){

    setTimeout(function(){
    $(‘.sidecart-subtotal-amount’).fadeIn(1000);
    },1000);
    }
    `

    Reply

    • Avatar

      Alex Podolyan

      |

      Hello Fabian,

      This is quite difficult to do.

      1. You need to add display:none for your cart counter of products in $fragments. This is code snippet works for me, but I don’t things that it will be work for you. You need change array key for your mini cart template

      $fragments['.tm_cart_widget .tm_cart_label .cart_ajax_data .total_products'] = '<div style="display:none">' . WC()->cart->get_cart_contents_count() . '</div>';

      2. In the $each that is responsible for replacing HTML, you need to add a check. If the key coincides with the number of items in the cart, we add fadeIn.

      if ( fragments ) {
          $.each( fragments, function( key, value ) {
              $( key ).replaceWith( value );
              if( key == '.tm_cart_widget .tm_cart_label .cart_ajax_data .total_products' ) {
                  $( key ).fadeIn(1000);
              }
          });
      }

      3. You also need to add a JS code to display the products count when you first load the page, and delete displat:none This is code snippet you need to insert beyond the click event.

      $( '.tm_cart_widget .tm_cart_label .cart_ajax_data .total_products' ).fadeIn(1000);

      I hope I was helpful 🙂
      Good luck, Alex

      Reply

      • Avatar

        Fabian

        |

        Hi Alex,

        thanks for your reply. I couldn’t achieve what I wanted by following your answer. You were talking about the “WC()->cart->get_cart_contents_count()”. But I need “WC()->cart->get_cart_subtotal();”. Adding “style=display:none;” to my subtotal div didn’t work.

        I used my fade in/out functions and added them to your Ajax widget remove click function. It is working, but the subtotal amount doesn’t fade in nicely after I remove a product from the mini-cart. And if remove all products form the cart, you can see only the subtotal div for a second. After a second it disappears and the “cart is empty” message appears. Guess I need another function for that or the delays are too long.

        How do I post code in your comments? Here is my js code (unformatted):

        mini-cart part for subtotal:

        cart->get_cart_subtotal(); ?>

        JS code:
        $(document).on(‘click’, ‘.sidecart-item-remove’, function (e){

        $.fade_out_subtotal();
        e.preventDefault();

        var product_id = $(this).attr(“data-product_id”),
        cart_item_key = $(this).attr(“data-cart_item_key”);

        $.ajax({
        type: ‘POST’,
        dataType: ‘json’,
        url: wc_add_to_cart_params.ajax_url,
        data: {
        action: “product_remove”,
        product_id: product_id,
        cart_item_key: cart_item_key
        },

        beforeSend: function() {
        $(‘#sidecart-item-id-‘+cart_item_key).fadeOut(500);
        },

        success: function(response) {
        setTimeout(function(){
        if ( ! response || response.error )
        return;

        var fragments = response.fragments;

        // Replace fragments
        if ( fragments ) {
        $.each( fragments, function( key, value ) {
        $( key ).replaceWith( value );
        $.fade_in_subtotal();
        });
        }

        }, 500);

        // Update cart
        $( document.body ).trigger( ‘wc_update_cart’ );

        },

        complete: function(){
        console.log(‘Item removed from sidecart’);
        }
        });

        });

        /**
        * Fade in/out subtotal in sidecart
        */

        $.fade_out_subtotal = function(){
        $(‘.sidecart-subtotal-amount’).fadeOut(500);
        }
        $.fade_in_subtotal = function(){
        setTimeout(function(){
        $(‘.sidecart-subtotal-amount’).fadeIn(500);
        }, 500);
        }

        Regards

        Fabian

        Reply

        • Avatar

          Alex Podolyan

          |

          All my comments are only recommendations, if I have an idea, I can voice it, but you should do the rest yourself.

          In similar cases, I never use setTimeout.

          If I want to change the value of something using fade, I am do of something like this

          $('.tm-wishlist-count').fadeOut( 500, function()
          {
              $(this).text("new count").fadeIn();
          });
          

          Reply

          • Avatar

            Fabian

            |

            Sorry, I didn’t mean to be rude. I am just a total beginner in programming and really appreciate your help! Keep up the good work!

  • Avatar

    Fabian

    |

    Me again. Still a beginner 🙁

    Would it be better to secure your Ajax function using “nonce”. Or is this not necessary?

    Regards Fabian

    Reply

    • Avatar

      Alex Podolyan

      |

      Hello Fabian,

      Yes, I thought about it and decided not to do it.

      In order to make product pages, cacheable WooCommerce sessions are not created until a cart is created.

      WooCommerce overrides one of the nonce parameters with a value which changes based on whether or not a WooCommerce session has been created.

      When you create the nonce for a new user with no cart and no session the nonce is calculated with one set of inputs. When you check the nonce after an item has been added to the cart the check value is generated with a different set of inputs because the WooCommerce session now exists. This causes a different nonce value to be generated and the nonce check against the old nonce value to fail.

      One workaround is to proactively create the WooCommerce session before creating the nonces. Note this could impact how your site is cached.

      Best, Alex

      Reply

      • Avatar

        Fabian

        |

        Hey Alex,

        thanks for your reply

        I think I get it. So security wise it isn’t a problem to not use nonce, because WooCommerce handles sessions by itself?

        Best Regards,

        Fabian

        Reply

        • Avatar

          Alex Podolyan

          |

          Hello Fabian,

          Yes you are right, besides this session is available only for you and nobody else can’t remove your products.

          Best Regards, Alex

          Reply

Leave a comment

Subscribe to our Newsletter

Something BIG is coming, you'll be the first to know about it.