Updating a total element to reflect selected price modifiers in your page

It can be useful to display a dynamically update total cost to customers on your add to cart pages - to let them know what the options they're selecting will cost. The following snippet will allow you to display a dynamic total to customers, taking into consideration a change in quantity or options that include a price product option modifier.

Step 1: Include snippet

Paste the following before the closing </body> tag of any page that you want to display the dynamically calculated price of a product. It can also be included in an existing javascript file you're including if you prefer. This snippet relies on the loader.js file being included on your page.

This is *not* required on the cart, checkout or receipt pages, so should not be place in those templates.

<script>
// Dynamic Price Calculation v3.1
var pricemod_regex=/[{\|]p([+\-:])([\d\.]+)(?:\D{3})?(?=[\|}])/,id_regex=/^(\d+):/,FC=FC||{};FC.onLoad=function(){FC.client.on("ready.done",initDynamicPrice)};
function initDynamicPrice(){ADJUST={};$("input,select").off("change.foxy-dynamic-price");$('form[action*="'+FC.settings.storedomain+'"]').each(function(){var b=$(this),d="",g={products:{}};$(this).find("[name='name'],[name^='name||'],[name$=':name'],[name*=':name||']").each(function(){var k=getId(this.name),c=k?k+":":"",e=parseFloat(b.find("[name='"+c+"price'],[name^='"+c+"price||']").first().val());e={id:k,code:"",base_price:isNaN(e)?0:e,quantity:1,attributes:{},has_quantity:!1};var h=b.find("[name='"+
c+"quantity'],[name^='"+c+"quantity||']");c=b.find("[name='"+c+"code'],[name^='"+c+"code||']");0<c.length&&(e.code=clearHash(c.first().val()),""===d&&(d=e.code));if(0<h.length){c=0;var l=getElementType(h);-1<["select","text"].indexOf(l)?(e.has_quantity=!0,c=parseFloat(clearHash(h.val()))):-1<["radio","checkbox"].indexOf(l)&&(e.has_quantity=!0,1==h.filter(":checked").length&&(c=parseFloat(clearHash(h.filter(":checked").val()))));isNaN(c)&&(c=0);e.quantity=c}g.products[k]=e});b.attr("data-fc-form-code")&&
(d=b.attr("data-fc-form-code"));""!==d&&($(this).find("input,select").each(function(){var b=getId(this.name),c=getName(this.name),e=getElementType($(this));if("quantity"==c)$(this).data("fc-adjust-for",d).on("change.foxy-dynamic-price",function(){var c=0;if(-1<["select","text"].indexOf(e)||-1<["radio","checkbox"].indexOf(e)&&$(this).is(":checked"))c=parseFloat(clearHash(this.value));isNaN(c)&&(c=0);ADJUST[$(this).data("fc-adjust-for")].products[b].quantity=c;recalcTotal()});else if("price"==c&&"hidden"!=
e)$(this).data("fc-adjust-for",d).on("change.foxy-dynamic-price",function(){var c=0;if(-1<["select","text"].indexOf(e)||-1<["radio","checkbox"].indexOf(e)&&$(this).is(":checked"))c=parseFloat(clearHash(this.value));isNaN(c)&&(c=0);ADJUST[$(this).data("fc-adjust-for")].products[b].base_price=c;recalcTotal()});else if("SELECT"==this.tagName){var h=!1;$(this).children("option").each(function(){-1<this.value.search(pricemod_regex)&&(h=!0)});h&&($(this).data("fc-adjust-for",d),g.products[b].attributes[clearHash(this.name)]=
clearHash(this.value),$(this).on("change.foxy-dynamic-price",function(){ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value);recalcTotal()}))}else if(-1<this.value.search(pricemod_regex))switch($(this).data("fc-adjust-for",d),$(this).attr("type")){case "checkbox":$(this).is(":checked")?g.products[b].attributes[clearHash(this.name)]=clearHash(this.value):g.products[b].attributes[clearHash(this.name)]="";$(this).on("change.foxy-dynamic-price",function(){$(this).is(":checked")?
ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value):ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]="";recalcTotal()});break;case "radio":g.products[b].attributes.hasOwnProperty(clearHash(this.name))||(g.products[b].attributes[clearHash(this.name)]=""),$(this).is(":checked")&&(g.products[b].attributes[clearHash(this.name)]=clearHash(this.value)),$("[name='"+this.name+"']").data("fc-adjust-for",d).on("change.foxy-dynamic-price",
function(){ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value);recalcTotal()})}}),ADJUST[d]=g)});recalcTotal()}function clearHash(b){return b.replace(/\|\|[\d\w]+(?:\|\|open)?$/,"")}function getNameParts(b){b=clearHash(b);return b.match(/(?:(\d+):)?(.*)/)}function getId(b){b=getNameParts(b);id_regex.test(this.name)&&(prefix=parseInt(this.name.match(id_regex)[0]));return void 0===b[1]?0:parseInt(b[1])}
function getName(b){return getNameParts(b)[2]}function getElementType(b){if("SELECT"==b[0].tagName)return"select";if("INPUT"==b[0].tagName)switch(b.attr("type").toLowerCase()){case "text":case "number":case "tel":return"text";default:return b.attr("type").toLowerCase()}}
function recalcTotal(){for(f in ADJUST){var b=0,d=0;for(p in ADJUST[f].products){var g=ADJUST[f].products[p].base_price,k=0;for(a in ADJUST[f].products[p].attributes){var c=ADJUST[f].products[p].attributes[a].match(pricemod_regex);if(c)switch(c[1]){case ":":g=parseFloat(c[2]);break;case "+":k+=parseFloat(c[2]);break;case "-":k-=parseFloat(c[2])}}g+=k;g*=ADJUST[f].products[p].quantity;b+=g;d+=ADJUST[f].products[p].quantity}"function"===typeof fcFormatPrice&&(b=fcFormatPrice(b,f));"function"===typeof fcFormatQuantity&&
(d=fcFormatQuantity(d,f));b="object"==typeof FC&&FC.hasOwnProperty("json")&&FC.json.config.hasOwnProperty("currency_format")?jQuery.trim(FC.util.money_format(FC.json.config.currency_format,b)):b.formatMoney(2);$("."+f+"_total").html(b);$("."+f+"_total_quantity").html(d)}}
Number.prototype.formatMoney=function(b,d,g){var k=this;b=isNaN(b=Math.abs(b))?2:b;d=void 0==d?".":d;g=void 0==g?",":g;var c=0>k?"-":"",e=parseInt(k=Math.abs(+k||0).toFixed(b))+"",h=3<(h=e.length)?h%3:0;return c+(h?e.substr(0,h)+g:"")+e.substr(h).replace(/(\d{3})(?=\d)/g,"$1"+g)+(b?d+Math.abs(k-e).toFixed(b).slice(2):"")};
</script>

Step 2: Set up forms

Setup your forms like so:

Product Code

Every FoxyCart based product that you want to dynamically calculate the total needs to have a unique code set like this:

<input type="hidden" name="code" value="p1" />

The code from the first product in your form will be used as a key for using in the element that dynamically shows the total product price.

If you're not able to know what product code is first, or you just want to set a specific code to use for the price modifier, you can also add a data attribute to your form like this:

<form ... data-fc-form-code="p1">

Total Price Element

Next, set an element to be where the total cost is shown and updated, by combining the code from above and “_total” as its class, like this:

<h4 class="p1_total">$20.00</h4>

If you'd like to show the total quantity, you can also do that using an element with a class that is a combination of the code and “_total_quantity” as its class, like:

<p class="p1_total_quantity">0</p>

Dynamically triggering recalculation

If for whatever reason you need to trigger the dynamic price calculations to be recreated - for example if you dynamically add in a new input that includes a price modifier, you can execute this function in your javascript:

initDynamicPrice();

That's it!

That should be it. The script basically runs through all the inputs within FoxyCart add to cart forms on the page, and grabs what the active value is. If that value has a price modifier (p:X, p+X, p-X), it chucks it into an array. After looping over all the form elements, it loops through all the selected price modifiers, adjusts the base price, multiplies it by the quantity and sets the total element.

Notes and Limitations

  • The snippet is run after the FoxyCart javascript has been initialized. As such - there may be a small time while the page is loading before the dynamic total is calculated.

Site Tools