Insert Product Price into the Add-to-Cart Button

Inject variation price into the add-to-cart button

Insert the product variation’s price into the add-to-cart button on the WooCommerce single product page. We’ll create a couple of JavaScript handlers to detect when the selected variation changes, and we’ll add options to hide the “clear variations” button, and the current variation price.

infoMake sure you’re using a custom child theme so you can edit “functions.php”.

How it’s going to work

The add-to-cart button for variable products looks like this:

<button type="submit" class="single_add_to_cart_button button alt">
	Add to basket
</button>

To dynamically show the price inside the button (after the text) we need to inject a small placeholder element, so the DOM fragment will look something like this:

<button type="submit" class="single_add_to_cart_button button alt">
	Add to basket
	<span class="atc-price"></span>
</button>

After we’ve injected the placeholder, we’ll listen for a couple of WooCommerce JavaScript events that fire when the selected variation changes, then update the contents of the placeholder.

Because we’re going to put the price inside the button, we probably want to hide the standard WooCommerce variation price – we’ll use a simple display:none; to do that.

Finally, we’ll add an option to get rid of the “Clear” button that resets the selected variation back to “none”.

The PHP code

In your custom child theme, create a file called wpt-price-in-add-to-cart.php and add the following. Check out the comments to see what each function does.

<?php

/**
 * WP Tutorials Price in Add-to-Cart Button (WPTPIAB)
 *
 * https://wp-tutorials.tech/refine-wordpress/insert-product-price-into-the-add-to-cart-button/
 */

defined('WPINC') || die();

const WPTPIAB_HIDE_CLEAR_VARIATION_LINK = true;
const WPTPIAB_HIDE_SELECTED_VARIATION_PRICE = true;

/**
 * Returns true on each page load that needs our JS/CSS assets.
 */
function wptpiab_is_variable_product_page() {
	$is_variable_product_page = false;

	if (!function_exists('WC')) {
		// WooCommerce is not installed. Do nothing.
	} elseif (!is_product() || empty(($product = wc_get_product()))) {
		// Not a product page. Do nothing.
	} elseif (!$product->is_type('variable')) {
		// Not a variable product. Do nothing.
	} else {
		$is_variable_product_page = true;
	}

	return $is_variable_product_page;
}

/**
 * Only add our script and CSS when showing a single product page for a
 * variable product.
 */
function wptpiab_enqueue_scripts() {
	if (wptpiab_is_variable_product_page()) {
		// Hide the link that clears the selected variation back to "none".
		if (WPTPIAB_HIDE_CLEAR_VARIATION_LINK) {
			add_filter('woocommerce_reset_variations_link', '__return_empty_string');
		}

		$handle = 'wptpiab';
		$base_uri = get_stylesheet_directory_uri();
		$theme_version = wp_get_theme()->get('Version');

		wp_enqueue_style($handle, $base_uri . '/wpt-price-in-add-to-cart/wptpiab-single-product-page.css', null, $theme_version);

		wp_enqueue_script(
			$handle,
			$base_uri . '/wpt-price-in-add-to-cart/wptpiab-single-product-page.js',
			['jquery'], // Our code is dependent on jQuery
			$theme_version
		);

		wp_localize_script(
			$handle,
			'wptpiabData', // This is the name of the global object we can access from the front-end JS.
			[
				'currencySymbol' => get_woocommerce_currency_symbol(),
				'symbolPosition' => get_option('woocommerce_currency_pos'),
				'decimals' => get_option('woocommerce_price_num_decimals'),
				'currentVariationPrice' => -1.0, // Start with an invalid price.
				'currentQuantity' => 1, // Start with a sensible quantity.
			]
		);
	}
}
add_action('wp_enqueue_scripts', 'wptpiab_enqueue_scripts');

/**
 * If we want to hide the price of the selected variation, we add a class
 * to the body, which we can pick up in our little stylesheet.
 */
function wptpiab_body_class($classes, $css_class) {
	if (wptpiab_is_variable_product_page() && WPTPIAB_HIDE_SELECTED_VARIATION_PRICE) {
		$classes[] = 'hide-variation-price';
	}

	return $classes;
}
add_filter('body_class', 'wptpiab_body_class', 10, 2);

We’ve created a helper function called wptpiab_is_variable_product_page() that returns true on any page-load that’s a single product page (for a variable product).

Our core functions is a handler for the wp_enqueue_scripts hook. This is how we add custom JavaScript and/or stylesheets to the HTML headers in WordPress. By testing wptpiab_is_variable_product_page() at the top of the function, we can add our assets only to the pages that need them (instead of to every page on the site).

We’ve also got a handler for the body_class filter so we can add a custom CSS class to the body element on our page.

Now edit your child theme’s functions.php file and add the following couple of lines somewhere near the top.

// Headwall WP Tutorials : Variation price inside the Add-to-cart button.
require_once dirname(__FILE__) . '/wpt-price-in-add-to-cart.php';

Modify the DOM in JavaScript

In your child theme, create a folder called “wpt-price-in-add-to-cart” and make a new file in there called “wptpiab-single-product-page.js“. Paste the following code into it:

/**
 * WP Tutorials Price in Add-to-Cart Button (WPTPIAB)
 * 
 * https://wp-tutorials.tech/refine-wordpress/insert-product-price-into-the-add-to-cart-button/
 *
 * Changelog
 * 
 * 2023-12-10 : Remove the initLoadDelay stuff. Add the ".atc-price" span
 *              element when it's first needed, instead of in init().
 *
 * 2023-11-21 : Added initLoadDelay so we can initialise the button after a
 *              short delay.
 *
 * 2023-11-13 : Completely restructured so we ignore the price_html that comes
 *              from WooCommerce. Instead, we build our own HTML price snippet
 *              based on the variation's price and the quantity.
 */

(function($) {
	'use strict';

	// Diagnostics
	// console.log('WPTPIAB : INIT');

	if (typeof wptpiabData === 'undefined') {
		console.error('WPTPIAB : Missing global data structure');
	} else {
		// Tidy up the data that have been passed from the back-end.
		wptpiabData.decimals = parseInt(wptpiabData.decimals);
		wptpiabData.currentQuantity = parseInt(wptpiabData.currentQuantity);
		wptpiabData.currentVariationPrice = parseFloat(wptpiabData.currentVariationPrice);

		// Diagnostics. Show the tidied-up parameters that have been pased from the back-end.
		// console.log(wptpiabData);

		wptpiabData.init = () => {
			// Triggered by WooCommerce when the user has selected a variation.
			$(document).on('found_variation', 'form.cart', wptpiabData.foundVariationHandler);

			// Triggered by WooCommerce when there's no variation selected.
			$(document).on('hide_variation', 'form.cart', wptpiabData.hideVariationHandler);

			// Standard event that's triggered when the value of the "quantity" input changes.
			$('input[name="quantity"]').on('change', wptpiabData.quantityChangedHandler);
		};

		wptpiabData.updatePriceInAddToCartButton = () => {
			const quantityInput = $('input[name="quantity"]');
			wptpiabData.currentQuantity = parseInt($(quantityInput).val());

			// If it doesn't already exist, create a span element within the
			// Add to Cart button that will hold the (HTML) variation price.
			let priceElement = $('.single_add_to_cart_button .atc-price');
			if (priceElement.length === 0) {
				priceElement = $('<span class="atc-price"></span>');
				$('.single_add_to_cart_button').append(priceElement);
			}

			// Remove everything that's already in our price element.
			$(priceElement).empty();

			// Either hide our price element, or show it and add the new price HTML.
			if (wptpiabData.currentVariationPrice <= 0.0 || wptpiabData.currentQuantity <= 0) {
				$('.single_add_to_cart_button .atc-price').hide();
			} else {
				$('.single_add_to_cart_button .atc-price').show();

				// Find the total price, rounded to the correct number of decimals.
				const factor = 10 ** wptpiabData.decimals * 1.0;
				const totalPrice = Math.round(wptpiabData.currentVariationPrice * wptpiabData.currentQuantity * factor) / factor;
				let priceHtml = '';

				// Format the price based on the currency symbol, and its position relative to the price.
				switch (wptpiabData.symbolPosition) {
					case 'right_space':
						priceHtml = `<span class=\"price\"><span class=\"woocommerce-Price-amount amount\"><bdi>${totalPrice}&nbsp;<span class=\"woocommerce-Price-currencySymbol\">${wptpiabData.currencySymbol}</span></bdi></span></span>`;
						break;

					case 'right':
						priceHtml = `<span class=\"price\"><span class=\"woocommerce-Price-amount amount\"><bdi>${totalPrice}<span class=\"woocommerce-Price-currencySymbol\">${wptpiabData.currencySymbol}</span></bdi></span></span>`;
						break;

					case 'left_space':
						priceHtml = `<span class=\"price\"><span class=\"woocommerce-Price-amount amount\"><bdi><span class=\"woocommerce-Price-currencySymbol\">${wptpiabData.currencySymbol}</span>&nbsp;${totalPrice}</bdi></span></span>`;
						break;

						// case 'left':
					default:
						priceHtml = `<span class=\"price\"><span class=\"woocommerce-Price-amount amount\"><bdi><span class=\"woocommerce-Price-currencySymbol\">${wptpiabData.currencySymbol}</span>${totalPrice}</bdi></span></span>`;
						break;
				}

				$('.single_add_to_cart_button .atc-price').append(priceHtml);
			}
		};

		wptpiabData.quantityChangedHandler = (event) => {
			wptpiabData.updatePriceInAddToCartButton();
		};

		wptpiabData.foundVariationHandler = (event, variation) => {
			// Diagnostics - show the variation object and all its properties.
			// There's some useful stuff in this object, but we only want display_price.
			// console.log(variation);

			// Grab the price from the variation object and store it in our wptpiabData object.
			wptpiabData.currentVariationPrice = variation.display_price;

			wptpiabData.updatePriceInAddToCartButton();
		};

		wptpiabData.hideVariationHandler = (event) => {
			// When no variation is selected, we set the price to an
			// invalid/negative price.
			wptpiabData.currentVariationPrice = -1;

			wptpiabData.updatePriceInAddToCartButton();
		};

		/**
		 * Our main entry point.
		 */
		$(window).on('load', function () {
			wptpiabData.init();
			wptpiabData.updatePriceInAddToCartButton();
		});
	}
})(jQuery);

The first thing we do is check that the global wptpiabData object has been passed from the back-end, with our parameters. Then we create some new functions and attached them to this object:

  • init()
    • Connect several events to our event-handler functions.
  • updatePriceInAddToCartButton()
    • Create a placeholder (a span element with class="atc-price") and inject it into the add-to-cart button.
    • Either hides the price from the add-to-cart button, or creates new HTML (price*quantity) and adds it to our atc-price element.
  • quantityChangedHandler()
    • Called when the value of input[name="quantity"] changes
  • foundVariationHandler()
    • Triggered by WooCommerce when the user has selected a valid variation
  • hideVariationHandler()
    • Triggered when the user has NOT selected a valid variation

The code should be easy to read through – with all the core logic inside the updatePriceInAddToCartButton() function.

Adding the styles

Finally, create a small stylesheet in “wpt-price-in-add-to-cart” called “wptpiab-single-product-page.css” and add the following:

/**
 * wpt-price-in-add-to-cart.css
 */

/* Hide the current variationn price */
.hide-variation-price form.cart .woocommerce-variation-price {
	display: none;
}

.single_add_to_cart_button .atc-price {
	margin-left: 0.5em;
}

Wrapping up

Use the two constants at the top of the PHP file enable/disable the various functions:

  • WPTPIAB_HIDE_CLEAR_VARIATION_LINK
  • WPTPIAB_HIDE_SELECTED_VARIATION_PRICE

The JavaScript has some diagnostic console.log() lines you can uncomment to see what’s happening. Otherwise it should be easy to read and modify, and the stylesheet is easy to extend.

Featured plugin

Product Variation Pills for WooCommerce
Replace the WooCommerce product variation drop-downs with user-focused, mobile-friendly radio/pill buttons. Create a cleaner product page and boost the user experience of your store!
Product radio buttons for WooCommerce

Have fun tweaking your single product page 😎👍

Like This Tutorial?

Let us know

WordPress plugins for developers

2 thoughts on “Insert Product Price into the Add-to-Cart Button”

  1. Thanks so much for this tuto. I tried, and it works well, except when you have a quantity button. Then the amount shown is only for one item. Do you know how to display the price for multiple items?

    Reply
    • I’m glad you liked the tutorial. I use this one a lot on my projects – it’s quite cool.

      I’ve updated the code today so we use the Quantity field now. It was quite a lot of work, because we need to “ignore” the price_html that WooCommerce gives us, then create our own HTML using price*quantity. We also need to use the correct currency symbol, and put it before-or-after the price.

      Replace the contents of “wpt-price-in-add-to-cart.php” and “wptpiab-single-product-page.js” with the new code that’s on this page and it should work fine 👍

      Reply

Leave a comment