Documentation You are here: start » v » 2.0 » javascript

This is an old revision of the document!


FoxyCart's JavaScript

FoxyCart's JavaScript files are full of goodies that you probably won't ever need to think twice about. But if default configurations are an anathema to you, if you need to get advanced for a custom integration, or if you're just curious about what's under the hood please explore this page and let us know if you have any questions.

WARNING: This page is pretty dense. Again, let us know if you need assistance.

The FoxyCart javascript is modular, and has a unified event system that's easy to hook into. The same javascript is used on your own site as well as on the FoxyCart-hosted cart, checkout, and receipt pages. If you want to hook into anything that FoxyCart does, you can use the FoxyCart events to do so.

Common Examples & Helpful Helpers

Making a JSONP Cart Request

If you like to add a product to the cart behind the scenes or with your own custom behaviors, there's a simple helper to make the request and get back the JSON you need to update things on your end. See the JSONP page for more information on this.

Resetting the FoxyCart Session & Cookies

If you'd like to completely clear out the FoxyCart session, you can call this.

FC.session.reset();

Validating an Input

Let's say you have a specific form with id=“my-specific-form” and an input inside that form with name=“date”. You could add this block of javascript (below) to your page to require a value on the date input.

<script>
var FC = FC || {};
FC.onLoad = function () {
    FC.client.on('cart-submit', function(params, next) {
        $element = $(params.element);
        if (
            $element.attr('id') == 'my-specific-form'
            && $element.find('[name="date"]').length > 0
            && !$element.find('[name="date"]').val()
        ) {
            alert('Date must be filled out');
        } else {
            next();
        }
    });
};
</script>

"Mini-Cart" Display and HTML Helper Functionality

If you'd like to have a persistent display showing your website visitor their current cart total (and a quick link to open the cart), you'd add something like this:

<p data-fc-id="minicart" style="display:none;">
	<a href="https://YOURSTORE.foxycart.com/cart?cart=view">
		<span data-fc-id="minicart-quantity">0</span>
		<span data-fc-id="minicart-singular"> item </span>
		<span data-fc-id="minicart-plural"> items </span>
		in cart. Total cost: $
		<span data-fc-id="minicart-order-total">0</span>
	</a>
</p>

The data attributes are the key. Stick those on any HTML element and it'll be updated automatically.

  • data-fc-id="minicart": If the FC.json.item_count is greater than 0, these elements will be shown. Otherwise they'll be hidden.
  • data-fc-id="minicart-empty": If the FC.json.item_count is 0 (the cart is empty), these elements will be shown. Otherwise they'll be hidden
  • data-fc-id="minicart-quantity": The inner HTML will be replaced by the value in FC.json.item_count.
  • data-fc-id="minicart-order-total": The inner HTML will be replaced by the value in FC.json.total_price, formatted using the _currency_format function (which adds decimals but doesn't add currency symbols).
  • data-fc-id="minicart-singular": The inner HTML will be shown if value in FC.json.item_count is equal to 1, and is hidden otherwise. It can be useful if you want to show “Item/Product” instead of “Items/Products” for FC.json.item_count = 1.
  • data-fc-id="minicart-singular": The inner HTML will be shown if value in FC.json.item_count is greater than 1 or less than 1 (zero), and is hidden otherwise.

Events: What they are and how to use them

Any time FoxyCart's javascript does anything you might need to hook into (do something before or after), we try to make that available as an event. The best way to understand this is by example.

Let's say you need to attach the FoxyCart session ID to some custom cart links. You can't do that on document ready / loaded event. To do this, you'd attach some custom code to the FoxyCart ready.done event, like this:

FC.client.on('ready.done', function() {
    // Custom code to attach the session ID to your links.
});

Another common example is product form validation that prevents a cart add from happening if required fields haven't been input. That'd look like this:

FC.client.on('cart-submit', function(params, next){
    // Check the product 
});

Available Events

  • Template events.
    • render.done: This event gets called after the template is updated. It gets passed an object with selector and block_id attributes. So FC.client.on('render.done', function(params){});, where params.selector and params.block_id exist.
  • Sidecart events.
    • sidecart-show: When the sidecart is displayed.
    • sidecart-hide: When the sidecart is hidden.
    • sidecart-detach: After the sidecart is hidden, the sidecart is actually detached from the DOM and this event fires.
  • Cart events.
    • cart-submit: When an item is added to the cart.
    • cart-item-quantity-update: When a quantity is changed in the cart
    • cart-item-remove: When a quantity is removed from the cart
    • cart-coupon-add: When a coupon is added
    • cart-coupon-remove: When a coupon is removed
    • cart-update: Called when the cart is updated. Things like rendering Twig templates or setting the FC.json.
    • cart-shipping-options-update: Called when the shipping options are updated
  • Checkout events.
    • checkout-submit: Called when the checkout is submitted
    • checkout-submit-enable: Triggered when the checkout submit button is re-enabled (if a checkout submit attempt is stopped due to validation errors).
    • checkout-submit-disable: Called when checkout submit button is clicked, disabling the button and triggering validations to run.
    • checkout-shipping-options-update: Called when the shipping options are updated on the checkout
  • Customer events. Called on the cart and the checkout.
    • customer-email-update: Called when an email address change triggers the email to be checked
    • customer-login: Called when a returning customer has successfully logged in.
    • customer-address-update: Triggered when a complete address is entered for an address type (like billling/shipping etc).
    • customer-address-change: Triggered when any of the customer address fields had been changed. params should include address type, like shipping/billing/multiship.
    • customer-shipping-option-select: called when a customer selects a shipping rate.

Accessing the FC object on your own website

The above events are available on your own site, but because the FoxyCart javascript loads asynchronously, the FC object (and thus all events) aren't defined on pageload or document ready. To work around this, you can define a method at FC.onLoad. The FC.onload method will be executed prior to the FC.client.init(). Here's a quick example:

var FC = FC || {};
FC.onLoad = function () {
	FC.client.on('ready.done', function () {
		// Your custom code goes here.
	});
};

Please note that this won't execute properly if you've got it inside a jQuery $(document).ready() call.

Event usage examples

Note that .on() and .off() methods can be chained.

Binding

FC.client.on("cart-item-remove", cartItemRemoveHandler)
         .on("cart-item-remove.done", cartItemRemoveDoneHandler)
         .on("cart-submit", cartSubmitHandler)
         .on("cart-submit.done", cartSubmitDoneHandler);

Unbinding

FC.client.off("cart-item-remove", cartItemRemoveHandler)
         .off("cart-submit", cartSubmitHandler);

Pausing and canceling the event queue

Synchronous handlers

Return false to stop processing the event queue.

FC.client.on("cart-item-remove", function (params) {
    if (params.itemPrice == 0) {
        alert("You cannot remove free products from your cart");
        return false;
    }
});
Asynchronous handlers

Declare your handler as a function with two parameters to convert it into an asynchronous handler.

The usual name for the second parameter is next.

The event queue will be paused until you call next() to continue processing the event queue or next(false) to stop it.

Note that returned value of an asynchronous handler will be ignored.

FC.client.on("cart-coupon-add", function (params, next) {
   $.ajax({
        type: "get",
        url: "http://api.example.com/check-coupon-availability",
        dataType: "jsonp",
        data: {
            couponCode: params.couponCode
        }
    }).done(function (data) {
        if (data.ok) {
            next();
        } else {
            alert('Sorry, your coupon is not available today.');
            next(false);
        }
    });
}).on("cart-coupon-add.done", function (params) {
    alert('Coupon '+params.couponCode+' has been added');
});

Getting the event

var event = FC.client.event("cart-item-remove");

Overriding default action

FC.client.event("cart-item-remove").override(newDefaultHandler);

Creating and triggering your own event

FC.client.wrap("your-own-event", function (params) {
    // body
});
FC.client.event("your-own-event").trigger(params);

Preventing product add to cart actions with custom criteria

Here's an example that prevents adding product category Product2 if there are products in the cart that belong to the category Product1.

FC.client.on('cart-submit', function(params, next){
	var product1_exists = false;
	if(!jQuery.isEmptyObject(FC.json.items)) {
		jQuery.each(FC.json.items, function(i, item){
			if(item.category == "Product1") {
				product1_exists = true;
			}
		});
	}
	if(params.data.category == "Product2" && product1_exists) {
		next(false);
	}
	else {
		next(true);
	}
});

Configuration and Loading Options

There are a few things that are set by default in FoxyCart's javascript, but that can be overridden. Typically, these are handled with the template config options in the admin, but there are a few situations where javascript overrides are needed or make more sense. To use these, add code like this before you include the FoxyCart javascript.

<script>
	var FC = FC || {};
	FC.override = FC.override || {};
 	FC.override.PROPERTY = VALUE;
 	FC.onLoad = function () {
 		// Attach FC event handlers here, before FC is defined. Like this:
 		FC.client.on('ready.done', function () {
 			// custom code goes here
 		});
 	};
 
</script>

The available properties are:

  • debug: If true, enables FoxyCart's FC.util.log() method so you can see what's going on. This setting is available as a template config value, but that value doesn't work on pageload on your own site (rather, it only works on the full-page cart, checkout, and receipt).
  • onLoad: Sometimes you'll need to attach an event handler once the FC object is loaded and ready. Whatever you put in the onLoad() function will be executed once, right before the FC.client.init() is called.

Sessions & Cookies

Most of the following is handled automatically, but if you need your FoxyCart customer sessions to only exist at certain (sub)domains or sections within your site, read on.

''sitedomain'': The Domain on Which Your Visitor's FoxyCart Cookies Are Set

The sitedomain is only used to determine where to set the cookie for the visitor's FoxyCart session. If your sitedomain is passed in as example.com or www.example.com, the fcsid (FoxyCart Session ID) cookie is set at the second-level domain, which is .example.com. If your sitedomain is a third-level domain like example.co.uk or subdomain.example.com, the cookie would be set at .example.co.uk or .subdomain.example.com.

This setting is only important if your site isn't at the second-level domain, or if you want to restrict your FoxyCart sessions by subdomain. For example, if your site is example.co.uk, you don't want the cookie set at .co.uk. Or if you want to have different FoxyCart sites at donations.example.com and products.example.com, you'd need the cookies not to be set at .example.com or the sessions would overlap with unexpected results. (Another example would be if your site is at a 3rd party provider like example.squarespace.com, you don't want all *.squarespace.com sites sharing your FoxyCart sessions.)

The only two things to keep in mind are:

  1. The www subdomain is effectively ignored, so if you do want to lock your sessions down to .www.example.com you'll need to set the sitedomain value to something like www1.example.com. The actual value doesn't matter; it's only counting the dots in the value after it strips the www.
  2. You cannot have the FoxyCart javascript at any other subdomains if you are isolating by subdomain. For example, if you have two separate stores at products.example.com and donations.example.com you cannot also have your loader.js at www.example.com or example.com, as that will set the session cookie at .example.com, which will override the products and donations session cookies.

Example: If you're testing two different sites at subdomains of the same domain (like store1.example.com and store2.example.com), you'd put this script tag before the FoxyCart script tag:

<script type="text/javascript" charset="utf-8">
var FC = {
  onLoad: function () {
    FC.settings.sitedomain = 'store1.example.com';
  }
}
</script>

Note: You probably wouldn't want to keep this in production, unless the site only lives at a subdomain, and is never available at the example.com top level domain.

''cookiepath'': The Path on Which Your Visitor's FoxyCart Cookies Are Set

The cookiepath determines the fcsid cookie's path. This is almost always going to be empty, so the cookies would be set at .example.com, but if you do need to have multiple FoxyCart sessions on the same domain you could use this setting to restrict cookies to something like .example.com/en/ and .example.com/es/ (to split English and Spanish FoxyCart stores, for example).

The cookiepath value must start and end in a / or you may run into issues, especially with Internet Explorer.

Example: Suppose you have a donations section and a bookstore, located at http://example.com/donations/ and http://example.com/bookstore/. (Note that the trailing slash is critical. If you have something like http://example.com/bookstore and http://example.com/donations this method will not work and the sessions will collide.) Add the following code before your calls to loader.js. Obviously, change out the “donations” with “bookstore” depending on where you're placing this code.

var FC = {
  onLoad: function () {
    FC.settings.cookiepath = '/donations/;
  }
}

Reference

JavaScript Naming Conventions

Similar to Google's conventions, but using snake_case instead of camelCase for variable names: Use functionNamesLikeThis, variable_names_like_this, ClassNamesLikeThis, EnumNamesLikeThis, methodNamesLikeThis, CONSTANT_VALUES_LIKE_THIS, foo.namespaceNamesLikeThis.bar, and filenameslikethis.js.

Event Naming Conventions

  • Present tense, like normal javascript events. So cart-coupon-add not cart-coupon-added.
  • Hyphens for the event names themselves.
  • The names should be “section” (cart, checkout, receipt), “noun” (coupon, email, address, etc.), modifier/attribute (if applicable, like cart-item-quantity, where quantity is the attribute of the item), and finally the verb (add, remove, update, change, etc.).
  • The “after” event gets the suffix “.done”: client.on(“cart-item-remove.done”, handler);

What Types of Events Are Available

If you find an event that isn't available to you but should be, let us know.

  • Anything in an init method.
  • Anything on change, click, keyup/down, blur, focusin/out.
  • Not selectors. Those aren't events. They return a string.
  • Rendering.
  • Interactions with API JSON.

File Structure

Internal pieces used to compile the external scripts. TODO: We'll be putting all this source on GitHub soon. Until then, you can access each file using the base url admin.foxycart.com/static/v/2.0.0/js/src/

  • fc.api.js: Methods that make requests to the api_json.php endpoint.
    • Internal methods: saveContactInfo, saveShippingServiceInfo, getShippingOptions, getTaxes
    • Events: none
  • fc.cart.js: Methods that make requests to the /cart endpoint, and methods that update the cart template (using Twig.js). Also initialization for the cart DOM, like setting behaviors for interacting with the cart.
    • Internal methods:
    • Events: cart-item-quantity-update, cart-item-remove, cart-coupon-add, cart-coupon-remove, customer-shipping-options-update, customer-address-update
  • fc.checkout.js: Checkout page initialization and methods.
    • Events: more info coming soon
  • fc.client.js: Methods for maintaining and handling the fcsid session value, and preparing links and forms to submit to the cart.
    • Events: cart-submit
  • fc.event.js: The event model, defined.
  • fc.locations.js: Shipping and billing country and state/province/region functionality.
    • Events:
  • fc.namespace.js: var FC = FC || {};
  • fc.postal-code.js: Methods to handle the postal code → city+state/region functionality.
  • fc.session.js: Methods to set the fcsid cookie.
  • fc.sidecart.js: Everything related to the Sidecart cart approach.
  • fc.template.js: Twig.js related methods. Also, custom Twig.js methods to mirror custom serverside methods.
  • fc.util.js: Helper functions.
    • Internal methods: money_format, str_pad, unserialize

External scripts

  • foxycart.js: namespace, session management, events, and utilities
  • foxycart.jsonp.js: Used as a base for custom integrations and for the default Pageslide approach.
  • foxycart.jsonp.pageslide.js: The cart in a Pageslide approach.

Pageload Events and Order of Execution

FC.client.init() is called on pageload for any CDN-loaded, store-specific javascript. This triggers:

  • FC.client.applySession()
  • FC.client.preventDefaultActions()
  • IF context==cart, then FC.client.updateCart()
    • FC.client.event('ready').trigger();
    • FC.client.updateMiniCart();

FC.client.updateCart() makes a FC.client.request to get the cart JSON

FC.client.request triggers FC.client.updateJSON() on completion.

FC.client.updateJSON() calls FC.client.updateMiniCart()

ready event triggers…

  • FC.cart.updateConfig();
  • FC.cart.updatePaymentInfoRequired();

FC.cart.updateConfig() checks and sets localStorage. Will trigger…

  • FC.client.requestConfig(), which is a call to FC.client._request that sets the config JSON with the updated data.

Twig.js, Template Rendering via JavaScript

Checkout

FC.checkout.render()
FC.checkout.renderLoginRegister()
FC.checkout.renderCustomerShipping()
FC.checkout.renderCustomerBilling()
FC.checkout.renderShippingRates()
FC.checkout.renderPaymentMethod()
FC.checkout.renderAdditionalFields()
FC.checkout.renderRequiredHiddenFields()

Cart

FC.cart.render()
FC.cart.renderShippingRates()
FC.cart.renderTaxes()
FC.cart.renderOrderTotals()
FC.cart.renderCouponEntry()
FC.cart.renderAddressEntry()
FC.cart.renderCartItemsDivs()

Sequential Rendering

You're allowed to make as many sequential render calls as you need. The real Twig rendering will be called only once. The following sequential commands

FC.cart.renderShippingRates();
FC.cart.renderTaxes();
FC.cart.renderOrderTotals();

will call the Twig renderer once, then the needed blocks will be replaced.

Site Tools