Чекбоксы в корзине Woocommerce

Сделаем чекбоксы и кнопки плюс/минус, а также свою анимацию на странице корзины woocommerce. НЕ ПОДХОДИТ для блочной темы. Корзину можно сделать шорткодом или виджетом корзина от Elementor.

snimok jekrana 2026 01 14 v 00.08.24 1

Добавляем сниппет код


/* -----------------------------
   Qty plus/minus buttons (cart)
------------------------------ */

add_action( 'woocommerce_after_quantity_input_field', function () {
	if ( is_cart() ) {
		echo '<button type="button" class="plus">+</button>';
	}
}, 25 );

add_action( 'woocommerce_before_quantity_input_field', function () {
	if ( is_cart() ) {
		echo '<button type="button" class="minus">−</button>';
	}
}, 25 );

/* ---------------------------------------------
   Enqueue JS + pass ajaxurl + nonce (cart only)
---------------------------------------------- */

add_action( 'wp_enqueue_scripts', function () {
	if ( ! is_cart() ) {
		return;
	}

	wp_enqueue_script(
		'cart-checkbox-selection',
		get_stylesheet_directory_uri() . '/js/cart-checkbox-selection.js',
		[ 'jquery' ],
		null,
		true
	);

	wp_localize_script( 'cart-checkbox-selection', 'my_cart_select', [
		'ajaxurl' => admin_url( 'admin-ajax.php' ),
		'nonce'   => wp_create_nonce( 'avs_cart_select_nonce' ),
	] );
} );

/* ---------------------------------------
   AJAX: remove unchecked cart items
---------------------------------------- */

add_action( 'wp_ajax_remove_unchecked_cart_items', 'avs_remove_unchecked_cart_items' );
add_action( 'wp_ajax_nopriv_remove_unchecked_cart_items', 'avs_remove_unchecked_cart_items' );

function avs_remove_unchecked_cart_items() {
	// 1) Validate payload
	if ( empty( $_POST['checked'] ) || ! is_array( $_POST['checked'] ) ) {
		wp_send_json_error( [ 'message' => 'No items received' ], 400 );
	}

	// 2) Nonce
	$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
	if ( ! $nonce || ! wp_verify_nonce( $nonce, 'avs_cart_select_nonce' ) ) {
		wp_send_json_error( [ 'message' => 'Bad nonce' ], 403 );
	}

	// 3) Cart exists
	if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
		wp_send_json_error( [ 'message' => 'Cart not available' ], 500 );
	}

	$checked_items = array_map(
		static function ( $v ) {
			return sanitize_text_field( wp_unslash( $v ) );
		},
		$_POST['checked']
	);

	$checked_map = array_flip( $checked_items );

	// 4) Decide what to remove
	$current_cart = WC()->cart->get_cart();
	$to_remove    = [];

	foreach ( $current_cart as $cart_key => $cart_item ) {
		if ( ! isset( $checked_map[ $cart_key ] ) ) {
			$to_remove[] = $cart_key;
		}
	}

	// 5) Remove
	foreach ( $to_remove as $cart_key ) {
		WC()->cart->remove_cart_item( $cart_key );
	}

	// 6) Recalculate totals + persist
	WC()->cart->calculate_totals();
	WC()->cart->set_session();

	if ( isset( WC()->session ) && method_exists( WC()->session, 'save_data' ) ) {
		WC()->session->save_data();
	}

	// 7) Remove invalid coupons (optional safety)
	$applied_coupons = WC()->cart->get_applied_coupons();
	foreach ( $applied_coupons as $coupon_code ) {
		try {
			$coupon = new WC_Coupon( $coupon_code );
			// If coupon object exists but is invalid, remove
			if ( method_exists( $coupon, 'is_valid' ) && ! $coupon->is_valid() ) {
				WC()->cart->remove_coupon( $coupon_code );
			}
		} catch ( Throwable $e ) {
			WC()->cart->remove_coupon( $coupon_code );
		}
	}

	// Recalculate again after coupon changes
	WC()->cart->calculate_totals();
	WC()->cart->set_session();

	if ( isset( WC()->session ) && method_exists( WC()->session, 'save_data' ) ) {
		WC()->session->save_data();
	}

	wp_send_json_success( [ 'message' => 'Cart updated' ] );
}

/* ---------------------------------------
   Replace "Order total" with selected total placeholder (cart only)
---------------------------------------- */

add_filter( 'woocommerce_cart_totals_order_total_html', function ( $value ) {
	if ( is_cart() ) {
		return '<span id="selected-total">0 р.</span>';
	}
	return $value;
} );

Добавьте js в дочернюю тему, создайте папку js
../public_html/wp-content/themes/hello-elementor-child/js/cart-checkbox-selection.js

Файл cart-checkbox-selection.js

jQuery(function ($) {

    const $doc  = $(document);
    const $body = $(document.body);

    const parsePrice = (str) =>
        parseFloat(String(str).replace(/[^\d.,]/g, "").replace(",", ".")) || 0;

    const stopScroll = () => {
        [0, 40, 120, 240, 400].forEach(t =>
            setTimeout(() => $("html, body").stop(), t)
        );
    };

    /* -------------------------
       АНИМАЦИЯ СТРОКИ ТОВАРА
    ---------------------------- */

    function breathRow($row) {
        $row.addClass("cart-row-breath");
    }
    function stopBreathRow($row) {
        $row.removeClass("cart-row-breath");
    }

    /* ----------------------------------------------------
       ГЛОБАЛЬНЫЙ СПИННЕР ТОЛЬКО В БЛОКЕ "ИТОГО"
    ------------------------------------------------------ */
    function showTotalsLoader() {
        if (!$(".cart-collaterals").length) return;

        // не плодим
        if ($(".totals-loader-overlay").length) return;

        $(".cart-collaterals").append(`
            <div class="totals-loader-overlay"></div>
            <div class="totals-loader-spinner"></div>
        `);
    }
    function hideTotalsLoader() {
        $(".totals-loader-overlay, .totals-loader-spinner").remove();
    }

    /* ---------- PLUS / MINUS -------- */

    function updateQuantityButtons() {
        $("input.qty").each(function () {
            const $input  = $(this);
            const $wrap   = $input.parent();
            const max = parseFloat($input.attr("max"));
            const min = parseFloat($input.attr("min"));

            if (max === 1 && min === 1) {
                $wrap.find("button.plus, button.minus").hide();
            } else {
                $wrap.find("button.plus, button.minus").show();
            }
        });
    }

    $doc.on("click", "button.plus, button.minus", function () {
        const $row = $(this).closest(".woocommerce-cart-form__cart-item");

        breathRow($row);

        const $input = $row.find(".qty");

        let val   = parseFloat($input.val());
        const max = parseFloat($input.attr("max"));
        const min = parseFloat($input.attr("min"));
        const step = parseFloat($input.attr("step")) || 1;

        if ($(this).hasClass("plus")) {
            val = max && val >= max ? max : val + step;
        } else {
            val = min && val <= min ? min : val - step;
        }

        $input.val(val).trigger("change");
    });

    $(".woocommerce").on("change", ".qty", function () {
        const $row = $(this).closest(".woocommerce-cart-form__cart-item");

        breathRow($row);

        $("[name='update_cart']").prop("disabled", false).trigger("click");
    });

    /* ----------- CHECKOUT BUTTON ----------- */

    function updateCheckoutButton() {
        const $btn = $(".checkout-button");
        if (!$btn.length) return;

        const sum = parsePrice($("#selected-total").text());
        let totalQty = 0;

        $(".cart-item-checkbox:checked").each(function () {
            const qty = parseFloat(
                $(this).closest(".woocommerce-cart-form__cart-item").find(".qty").val()
            ) || 0;
            totalQty += qty;
        });

        if (sum > 0 && totalQty > 0) {
            $btn.html(`
                К оформлению
                <div class="checkout-summary">${totalQty} шт., ${sum} р.</div>
            `);
        } else {
            $btn.html("К оформлению");
        }
    }

    /* ----------- TOTAL CALCULATION (Woo standard prices) ----------- */

    function getRowLineSubtotal($row) {
        // Стандартно Woo кладёт сумму строки (цена * qty) сюда:
        const $sub = $row.find(".product-subtotal .amount").first();
        if ($sub.length) return parsePrice($sub.text());

        // Фолбэк: (цена за единицу * qty)
        const qty = parseFloat($row.find(".qty").val()) || 0;
        const unitPriceText =
            $row.find(".product-price .amount").first().text() ||
            $row.find(".amount").first().text();

        return (parsePrice(unitPriceText) * qty) || 0;
    }

    function recalcSelectedTotal() {
        let selectedRaw = 0;
        let allRaw = 0;

        // subtotal/discount из totals (как у Woo), чтобы учесть купоны пропорционально
        const subtotal = parsePrice($(".cart-subtotal .amount").first().text());
        const discount = parsePrice($(".cart-discount .amount").first().text());

        const wcTotal  = subtotal - discount;
        const discountRatio = subtotal ? wcTotal / subtotal : 1;

        $(".woocommerce-cart-form__cart-item").each(function () {
            const $row = $(this);

            const rowLineSubtotal = getRowLineSubtotal($row);
            allRaw += rowLineSubtotal;

            if ($row.find(".cart-item-checkbox").prop("checked")) {
                selectedRaw += rowLineSubtotal;
            }
        });

        // Если выбрано всё — показываем итог Woo после купона.
        // Если выбрана часть — применяем скидку пропорционально.
        let finalTotal =
            !selectedRaw ? 0 :
            selectedRaw === allRaw ? wcTotal :
            selectedRaw * discountRatio;

        // защита от -0 и отрицательных значений
        if (!isFinite(finalTotal) || finalTotal < 0) finalTotal = 0;

        $("#selected-total").text(finalTotal.toFixed(0) + " р.");

        updateCheckoutButton();
    }

    /* ----------- CHECKBOXES ----------- */

    function insertCartCheckboxes() {
        $(".woocommerce-cart-form__cart-item").each(function () {
            const $row = $(this);

            if ($row.find(".cart-item-checkbox").length) return;

            const input = $row.find("input.qty").attr("name");
            if (!input) return;

            const m = input.match(/\[(.*?)\]/);
            if (!m) return;

            const key = m[1];

            $row.prepend(`
                <td class="product-select">
                    <input type="checkbox" class="cart-item-checkbox" value="${key}" checked>
                </td>
            `);
        });

        if (!$(".select-all-wrapper").length) {
            $(".shop_table thead tr").prepend(`
                <th class="product-select">
                    <div class="select-all-wrapper">
                        <input type="checkbox" id="cart-select-all" checked>
                        <label for="cart-select-all" class="select-all-text">Выбрать всё</label>
                    </div>
                </th>
            `);
        }
    }

    function updateSelectAll() {
        const total   = $(".cart-item-checkbox").length;
        const checked = $(".cart-item-checkbox:checked").length;
        $("#cart-select-all").prop("checked", total > 0 && total === checked);
    }

    /* ----------- CART UPDATE ----------- */

    function afterCartUpdate() {
        // выключаем дыхание сразу после перерисовки Woo
        $(".cart-row-breath").removeClass("cart-row-breath");

        hideTotalsLoader();

        insertCartCheckboxes();
        updateQuantityButtons();
        updateSelectAll();
        recalcSelectedTotal();
        updateCheckoutButton();
        stopScroll();
    }

    afterCartUpdate();

    /* -------------------------
       HOOKS
    ---------------------------- */

    $body.on("updated_cart_totals", afterCartUpdate);

    $body.on("applied_coupon removed_coupon", function () {
        showTotalsLoader();
        setTimeout(afterCartUpdate, 300);
    });

    /* --- удаление товара — дыхание строки товара --- */
    $doc.on("click", ".product-remove a", function () {
        const $row = $(this).closest("tr");
        breathRow($row);
    });

    $doc.on("change", ".cart-item-checkbox", function () {
        updateSelectAll();
        recalcSelectedTotal();
    });

    $doc.on("change", "#cart-select-all", function () {
        $(".cart-item-checkbox").prop("checked", this.checked);
        recalcSelectedTotal();
    });

    /* ----------- CHECKOUT ----------- */

    $doc.on("click", ".checkout-button", function (e) {
        const checked = $(".cart-item-checkbox:checked")
            .map(function () { return $(this).val(); })
            .get();

        if (!checked.length) {
            alert("Выберите хотя бы один товар!");
            return false;
        }

        // опционально: проверка класса недоступности (оставил как у вас)
        let hasUnavailable = false;
        $(".cart-item-checkbox:checked").each(function () {
            const $row = $(this).closest(".woocommerce-cart-form__cart-item");
            if ($row.hasClass("avs-outofstock-item")) {
                hasUnavailable = true;
                return false;
            }
        });

        if (hasUnavailable) {
            alert("В заказе есть товары, которые недоступны к оформлению");
            return false;
        }

        e.preventDefault();
        showTotalsLoader();

        $.post(my_cart_select.ajaxurl, {
            action: "remove_unchecked_cart_items",
            checked,
            nonce: my_cart_select.nonce
        }, () => window.location.href = "/checkout");
    });

});

И самое сложно это css. Ниже код для анимации:

 /* Отключить стандарт анимацию*/
.woocommerce .blockUI.blockOverlay:before,
.woocommerce .loader:before {
    display: none !important;
    content: none !important;}
.blockUI.blockOverlay {
    background: transparent !important;
    opacity: 0 !important;
    pointer-events: none !important;}
/* своя анимация */
.totals-loader-overlay {
    position: absolute;
    inset: 0;
    background: rgba(255,255,255,0.35);
    z-index: 50;}
.totals-loader-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 32px;
    height: 32px;
    border: 4px solid #6A6A6A;
    border-right-color: transparent;
    border-radius: 50%;
    animation: woospin 0.6s linear infinite;
    transform: translate(-50%, -50%);
    z-index: 60;}
@keyframes woospin {
    0%   { transform: translate(-50%, -50%) rotate(0deg); }
    100% { transform: translate(-50%, -50%) rotate(360deg); }}
/* Анимация мигание */
.cart-row-breath {animation: cartBreathing 1.3s ease-in-out infinite;}
@keyframes cartBreathing {
    0%   { opacity: 1; }
    30%  { opacity: 0.6; } 
    60%  { opacity: 0.85; }
    100% { opacity: 1; }}
    
/* Отключить уведомления */
.woocommerce-notices-wrapper{display:none!important;}

И css визуально для корзины:

.cart-subtotal{display:none;}
tr.cart-subtotal, .cart_totals h2, .product-price{display:none!important;}

table.cart img{aspect-ratio:1/1;object-fit: cover;border-radius:8px;border:1px solid#C5C5C5;}
.remove{padding:4px;}

/*кол-во*/  
 input::-webkit-inner-spin-button{display:none;}
selector .input-text.qty {text-align:center;width:38px;}
.quantity{background-color:#D2DEE8; padding:2px;border-radius:8px;text-align:center;display: inline-flex; align-items:end; justify-content: center;}
selector .minus,selector .plus{background-color:transparent;font-size: 22px;padding:1px 9px;border-radius:8px;font-family: YS Text,Helvetica,Arial,sans-serif;color:#000;border:none;font-weight:500;}

.shop_table_responsive tr td, .woocommerce-page table.shop_table_responsive tr td{text-align:left!important;}
tr.selected-total-row{background-color:#fff;}
.woocommerce-page table.shop_table_responsive thead {display: table-header-group;}
#selected-total{font-size:18px;color:#000;}
tr.order-total ::before{font-weight:bold!important;font-size:18px!important;}

/*чекбоксы*/
.cart-item-checkbox, #cart-select-all {
    appearance: none;
    -webkit-appearance: none;
    outline: none;
    width: 22px;
    height: 22px;
    border: 2px solid #b3b3b3;
    border-radius:6px;
    background: #E4EBF1;
    cursor: pointer;
    position: relative;
    vertical-align: top;}
.cart-item-checkbox:checked, #cart-select-all:checked {
    background: #89ADE0;
    border-color: #89ADE0;}
.cart-item-checkbox:checked::after,
#cart-select-all:checked::after {
    content: '';
    display: block;
    position: absolute;
    left: 6px;
    top: 3px;
    width: 6px;
    height: 10px;
    border: solid #fff;
    border-width: 0 3px 3px 0;
    transform: rotate(45deg);}

/*К оформлению*/
.checkout-button .checkout-summary {font-size:13px;opacity: 0.9;margin-top:3px;font-weight:500;}
/*выбрать всё*/
.select-all-wrapper {
    position: relative;
    width: 22px;       
    height: 22px;}
.select-all-text {
    position: absolute;
    left: 36px;       
    top: 50%;
    transform: translateY(-50%);
    white-space: nowrap;
    cursor: pointer;
    font-size: 14px;
    margin: 0;}

 /*для компов*/  
@media (min-width: 1200px){
.product-remove {font-size:20px!important;width:26px;}   .product-name {text-align:left;} .product-quantity {width:136px;}  table.cart img {width:120px!important;} .product-thumbnail{width:140px;}.shop_table {border-spacing:0 8px;} 
td.product-select {position: relative; width: 1px;}
.cart-item-checkbox {position: absolute;top: 12px;  left: 12px;}
    td.product-select,th.product-select{padding-left:12px!important;max-width:20px;}}

@media (min-width: 961px) and (max-width: 1199px) {.product-remove {font-size:20px!important;width:20px;} .product-quantity {width:136px;} .product-subtotal {width:40px;}table.cart img {width:60px!important;} .product-thumbnail{width:80px;}.shop_table {border-spacing:0 8px;}
    td.product-select {position: relative; width: 1px;}
.cart-item-checkbox {position: absolute;top: 12px;  left: 12px;}
td.product-select,th.product-select{padding-left:12px!important;max-width:20px;}}
 
@media (min-width: 769px) and (max-width: 960px) {selector .product-remove {font-size:20px!important;width:20px; text-align:left!important;}  selector .product-thumbnail {width:65px;}  selector .product-quantity {width:150px;} selector .product-subtotal {width:70px; text-align:center;}selector table.cart img {width:150px;} selector .product-thumbnail{width:150px;}.shop_table {border-spacing:0 8px;}
    td.product-select {position: relative; width: 1px;}
.cart-item-checkbox {position: absolute;top: 12px;  left: 12px;}
td.product-select,th.product-select{padding-left:12px!important;max-width:20px;}}

/*для  мобилок*/  
@media (max-width: 768px) {
th.product-name, th.product-quantity, th.product-subtotal{display:none;}
.cart td:before{display:none;}
span.woocommerce-Price-amount{font-size: 13px!important;}
.cart tr{background-color:#E4EBF1;border-radius:14px;margin-bottom:8px;}
.quantity {width:118px;}
.qty {width:44px!important;}
.wc-proceed-to-checkout {position: fixed;
bottom: 54px;left: 24px;right: 24px; z-index: 999;}
    .woocommerce table.cart,
 .woocommerce table.cart tbody {width: 100%;}
.woocommerce table.cart tr.woocommerce-cart-form__cart-item.cart_item {
    display: block;        
    position: relative;    
    padding: 12px 12px 16px 12px;
    margin-bottom: 10px;
    background: #E4EBF1;
    border-radius: 12px;}
  .woocommerce table.cart td.product-thumbnail {
    display: block !important;
    float: left;
    width: 90px !important;
    padding: 0 !important;
    margin-right: 12px;
    flex: none;}
  .woocommerce table.cart td.product-thumbnail img {
    width: 90px !important;
    object-fit: cover;
    border-radius: 10px;
    display: block;}
  .woocommerce table.cart td.product-name,
  .woocommerce table.cart td.product-quantity,
  .woocommerce table.cart td.product-subtotal {
    display: block !important;
    margin-left: 100px;
    padding: 0 !important;
    clear: none;}
  .woocommerce table.cart td.product-quantity {
    margin-top: 11px;}
  .woocommerce table.cart td.product-quantity .quantity {
    margin: 0;}
  .woocommerce table.cart td.product-subtotal {
    margin-top: 4px;}
  .woocommerce table.cart td.product-remove {
    position: absolute;
    right: 12px;
    top: 0px;
    width: auto;
    z-index: 10;
    display: block;}
  .woocommerce table.cart td.product-remove .remove {
    font-size: 30px;}
  /* Очистка float после каждой строки */
  .woocommerce table.cart tr.woocommerce-cart-form__cart-item.cart_item::after {
    content: "";
    display: block;
    clear: both;}}

@media (max-width: 480px){.wc-proceed-to-checkout{bottom:43px;left:4px;right: 4px;}}

.elementor-widget-woocommerce-cart .woocommerce table.cart tr{padding:16px 12px;}

/* Строки товаров*/
tr.woocommerce-cart-form__cart-item.cart_item{background:#E4EBF1;}
.woocommerce-cart-form__cart-item td:first-child {border-top-left-radius: 14px;
  border-bottom-left-radius: 14px;}
.woocommerce-cart-form__cart-item td:last-child {border-top-right-radius: 14px;
  border-bottom-right-radius: 14px;}
  

Настройки для компа и мобилки.
Слева — для компа.

Типография

snimok jekrana 2026 01 14 v 00.17.17
snimok jekrana 2026 01 14 v 00.21.38

Итог заказа

snimok jekrana 2026 01 14 v 00.22.49
snimok jekrana 2026 01 14 v 00.22.36

Итого

snimok jekrana 2026 01 14 v 00.26.21
snimok jekrana 2026 01 14 v 00.26.27

Настроить: сводка заказов

одинаково для компа и мобилки

snimok jekrana 2026 01 14 v 00.30.38

Настроить: Итоги

snimok jekrana 2026 01 14 v 00.32.14
snimok jekrana 2026 01 14 v 00.32.25

Также в корзине, если применён купон, то его условия проверяются при нажатии на кнопку «Перейти к оформлению», так как чекбоксами можно отметить меньше товаров и это нарушит условия купона.

Picture of Автор: Александра

Автор: Александра

@avsalexandra
Занимаюсь натуральным питанием собак и кошек BARF. Wordpress для души ☺️

Crocoblock
Elementor
Gutenberg
Jetengine
Jetformbuilder
profile builder
Woocommerce
Wordpress
WYSIWYG
Лейка
#автосохранение
#доменная почта
#рассылка
#бейдж
#благотворительность
#заказ ожидает
#подарок
#подчёркивание
#публикация постов
#видео
#пожертвования
#мультивыбор
#роли
#drag and drop
#изображения товаров
#подписки
#распродажа
#личный кабинет
#пагинация
#alt text
#галерея товара
#аватар
#возврат
#видео товара
#купон
#отменить заказ
Комментарии:

Добавить комментарий