WooCommerce Plus/Minus Quantity Buttons Not Working (Even After a Cart Update)

Quick Summary

  • Attach one click handler to document and delegate it to .quantity .plus and .quantity .minus, so it keeps working after WooCommerce replaces the cart HTML.
  • Change the value with the input's stepUp() and stepDown(), which respect the min, max and step WooCommerce prints, then trigger change so Update cart becomes active.
  • Call preventDefault() so buttons without type="button" don't submit the form; the Cart block has its own buttons and isn't affected.

WooCommerce doesn’t add plus and minus buttons to the quantity field; they come from your theme or your own markup. When they do nothing, or work until the cart updates and then stop, the fix is the same: attach one delegated click handler to document, so it keeps working when WooCommerce replaces the cart HTML, and let the input’s own stepUp() and stepDown() change the value within its min, max and step.

Add the code to your theme’s JavaScript file (for example a custom.js file in your child theme that you enqueue with jquery as a dependency).

Make the plus and minus buttons work

JavaScript
jQuery(function ($) {
    $(document).on('click', '.quantity .plus, .quantity .minus', function (e) {
        e.preventDefault();

        var input = $(this).closest('.quantity').find('input.qty').get(0);
        if (!input || input.type !== 'number' || input.readOnly) {
            return;
        }

        var oldValue = input.value;
        if ($(this).hasClass('plus')) {
            input.stepUp();
        } else {
            input.stepDown();
        }

        if (input.value !== oldValue) {
            $(input).trigger('change');
        }
    });
});
  • jQuery(function ($) { ... }) runs the code when the page is ready and makes $ available. WordPress loads jQuery in no-conflict mode, so a bare $ outside the wrapper causes TypeError: $ is not a function.
  • The code attaches the handler to document and delegates it to .plus and .minus inside any .quantity box. It finds the input with closest('.quantity').find('input.qty'), so the buttons can sit before or after the input.
  • e.preventDefault() matters when the buttons are <button> elements without type="button". A button inside a form submits it by default, which reloads the page or adds the product to the cart instead of changing the number.
  • stepUp() and stepDown() are native methods of number inputs. They respect the min, max and step attributes WooCommerce prints, so the quantity stops at the product’s minimum (1 on a product page, 0 in the cart, where 0 removes the item) and at its stock limit.
  • If the input is hidden (a product sold individually) or read-only, nothing happens, because the quantity can’t change.
  • .trigger('change') tells WooCommerce the value changed, as if the customer had typed it. On the cart page, WooCommerce’s cart script listens for that event and enables the Update cart button.

Why the buttons stop working after the cart updates

When the customer clicks Update cart or removes an item, WooCommerce reloads the cart over AJAX and replaces the whole .woocommerce-cart-form with new HTML. Any handler bound directly to the old elements disappears with them. That is what happened with code like this:

JavaScript
$('.quantity').on('click', '.plus', function () {
    // ...
});

It is attached to the .quantity elements that exist on page load. After the first AJAX update those elements are gone, and the new ones have no handler, so the buttons do nothing.

An older fix was to attach the handlers again inside jQuery(document).ajaxComplete(), calling .off() first so one click didn’t trigger the handler several times. That works, but it re-runs after every AJAX request on the page. A handler delegated from document, as above, never needs rebinding: document is never replaced, and jQuery checks the clicked element against the selector at click time. You also don’t need your own code to enable the Update cart button; triggering change is enough.

If you need to run other code after WooCommerce refreshes the cart, it triggers the updated_wc_div event on document.body once the new cart HTML is in place:

JavaScript
jQuery(function ($) {
    $(document.body).on('updated_wc_div', function () {
        // The new cart HTML is in the page now.
    });
});

Adding the buttons yourself

If your theme has no plus and minus buttons, you can print them with the two hooks in WooCommerce’s quantity template, woocommerce_before_quantity_input_field and woocommerce_after_quantity_input_field. They run inside the .quantity wrapper, right before and after the input. Add this to your child theme’s functions.php:

PHP
add_action( 'woocommerce_before_quantity_input_field', function () {
    echo '<button type="button" class="minus" aria-label="Decrease quantity">-</button>';
} );

add_action( 'woocommerce_after_quantity_input_field', function () {
    echo '<button type="button" class="plus" aria-label="Increase quantity">+</button>';
} );

The buttons then work with the JavaScript above. They are added wherever the classic quantity template is used, including for products sold individually, where the input is hidden; hide the buttons there with CSS if your theme shows them.

Things to check

  • This is for the classic cart (the [woocommerce_cart] shortcode) and classic product templates. The Cart block and the Add to Cart with Options block have their own plus and minus buttons, and this code doesn’t affect them.
  • If your theme or a plugin already binds its own plus and minus handlers, each click can change the quantity twice. Remove one of them.
  • Your buttons need the classes plus and minus and must be inside the .quantity wrapper. Change the selectors if your markup uses other names.
  • To gray out the minus button when the quantity is at 1, see disabling the minus button at quantity 1.
  • After changing a theme JavaScript file, clear any page or asset cache so the browser loads the new version.

If your cart’s quantity controls or other interactive parts need fixing or rebuilding, see my WooCommerce development services.

Share on X (opens in a new tab) Share on LinkedIn (opens in a new tab) Share on Facebook (opens in a new tab)

ChatGPT (opens in a new tab) Claude (opens in a new tab) Gemini (opens in a new tab) Perplexity (opens in a new tab) Grok (opens in a new tab)

More Insights

See More