WooCommerce product add to cart consent pop-up

WooCommerce handles its add-to-cart process with AJAX, which is great and all, but how do we interrupt that AJAX process to add in our last-minute validation/verification? Inside woocommerce/assets/js/frontend/add-to-cart.js line 73, there’s the code:

<?php
// Allow 3rd parties to validate and quit early.
if ( false === $( document.body ).triggerHandler( 'should_send_ajax_request.adding_to_cart', [ $thisbutton ] ) ) {
    $( document.body ).trigger( 'ajax_request_not_sent.adding_to_cart', [ false, false, $thisbutton ] );
    return true; 
}

Cool! WooCommerce developers put that there to help us intercept add-to-cart requests. That means all we’d need to do is write some JavaScript to return false for the ‘should_send_ajax_request.adding_to_cart’ triggerHandler(). Something like this:

jQuery( "body" ).on( "should_send_ajax_request.adding_to_cart", function( e, $button ) {
    if ( $( "body" ).hasClass( 'our-condition-is-not-met' ) ) { // random example
        return false;
    } 
    return true;
});

This would work in a lot of scenarios, but is limited to frontend scripting and gives no access to the server, so it’s a bit handicapping. Plus, we are already stuck inside the WooCommerce add-to-cart JavaScript, so we can’t run other scripts…

I wanted to bring up a JavaScript window.confirm() dialog box asking the customer to confirm something before the item could be added to the cart. By the time this trigger is fired, it’s too late to run a dialog on screen. So I did something a little tricky.

I added a new “fake” add-to-cart button (with CSS class “custom_add_to_cart_button”) to the product page, and hid the real WooCommerce add-to-cart button. The fake button would trigger the dialog, and if the customer confirmed, I’d trigger a click of the real button, which could stay hidden.

I only wanted to do this on a specific WooCommerce simple product (ID #5653), and so I edited the woocommerce/single-product/add-to-cart/simple.php template:

<?php if ( is_single( 5653 ) ) { ?>

    <button type="button" class="custom_add_to_cart_button button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>

    <button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="d-none single_add_to_cart_button button alt"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>

<?php } else { ?>

    <button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button button alt"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>

<?php }

Since I’m using Bootstrap you can see how I hid the native “.add-to-cart” button using the “d-none” class. When not using Bootstrap you could just hide the native WooCommerce .single_add_product_to_cart button using CSS:

.postid-5653 .single_add_to_cart_button{display:none}

Instead of editing WooCommerce templates, which can lead to headaches down the road if WooCommerce makes breaking changes — or even if they just keep updating the template — this could also be done by using the 'woocommerce_after_add_to_cart_quantity' action hook and the $product global, like so:

<?php add_action( 'woocommerce_after_add_to_cart_quantity', 'custom_button_after_add_to_cart_quantity' );

function dummy_button_after_add_to_cart_quantity() {
    global $product;
    if ( ! $product || 5653 !== $product->get_id() ) {	
        return;	
    } ?>
			<button type="button" class="custom_add_to_cart_button button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>
<?php }

Finally, I queue up my custom jQuery to handle the add-to-cart click and show the confirmation dialog. If the customer doesn’t confirm, nothing happens. Otherwise, the real WooCommerce add-to-cart button is clicked and the item is added to the cart (assuming it is in stock, etc).

jQuery(document).ready(function($){
    $( ".custom_add_to_cart_button" ).on( 'click', function(e, $button) {
        if ( ! window.confirm( "I understand this product is likely to blow my mind. I am prepared!" ) ) {
            return;
        } $( '.single_add_to_cart_button' ).trigger( 'click' );
    });
});

Sometimes we just have to get a little tricky to make customizations to WooCommerce shops without over-complicating things. Adding another button to the product page doesn’t hurt anything. Worst case scenario is someone, maybe someone using a screen reader, clicks the real WooCommerce button (because though it is hidden, it is still in the DOM) and proceeds directly to the cart without interacting with the dialog. That would be a very rare case, or might happen in situations where your dialog is enforcing something people don’t want enforced (in which case they will seek work-arounds in the DOM). Anyway, in my case it’s really not something to worry about; I’m just pointing out that this solution, like most online consents, is not bullet-proof.

All said sometimes there are specific details or disclaimers about our WooCommerce products that we really need customers to know about before they buy. Examples might be:

  • “I understand this item has a 3-week lead time,” or
  • “I understand this product is not recommended for children under the age of 8”, or
  • “I understand that this product can be damage vision and cause blindness if shined into eyes”

Forcing customers to consent to these important points can prevent heartache, disputes and returns, so often it’s worthwhile to program in a dialog. If you have a lot of products it might be worth using a plugin for this feature, such as Product Disclaimer for WooCommerce. But especially if you just need this for one product it’s worthwhile to just write out a dozen short lines of code or so to get it done, like I did.