Shopp has built-in support for AJAX, but has it disabled by default for the widest possible compatibility out-of-the-box. This guide will show you how to unlock the power of AJAX in your Shopp.

Using Shopp AJAX Add to Cart Behaviors

ajax-cart

Using Shopp AJAX Add to Cart Behaviors

Shopp makes it easy to turn the AJAX-enabled cart on by customizing theme template files. Here’s how:

How It Works

AJAX (asynchronous JavaScript and XML) is a term used to describe website development techniques for requesting and processing information in the background without requiring a page redirect (or refresh).

Shopp’s shopping cart has support for returning information asynchronously back to the client (the web browser) using specially formatted cart requests via JavaScript. When activating the appropriate element (usually the Add to Cart button), the AJAX request is made to Shopp in the background and Shopp returns results back for the JavaScript to handle.

Enabling Basic AJAX

Shopp already has default AJAX request and handling routines built-in to send requests to the cart and handle responses from the cart. You’ll need to make sure the sidebar cart is enabled by turning on the Shopp Cart widget or by adding the following Theme API call to your WordPress theme’s sidebar.php file directly (Note: there can be multiple cart widgets OR one side-cart but not both):

<?php shopp('cart','sidecart'); ?>

This is the cart display that will show the updates when an AJAX request is sent. To enable the AJAX add to cart behavior, simply find the shopp(‘product’,’addtocart’) tags typically found in the category.php template file and product.php template file and add the ajax option like so:

For JSON response (the default)

<?php shopp('product','addtocart','ajax=on'); ?>

For JSON response (explicitly)

<?php shopp('product','addtocart','ajax=json'); ?>

For HTML response

<?php shopp('product','addtocart','ajax=html'); ?>

Now when visiting your store, clicking the Add to Cart buttons that have the ajax=on option will show the AJAX request in action. The product will be added to the cart in the background. At this point you’ll see the default behavior for handling AJAX responses from Shopp. The item thumbnail will appear with the item name (variation name) and the unit price in the sidecart or Cart Widget(s).

Customizing AJAX Behaviors

If for some reason, you do not like or need to change some aspect of the default AJAX behaviors in Shopp, this section should give you some additional information on how Shopp cart ajax is working, so you can override the built-in functionality.

Shopp has two JavaScript functions for making and handling cart requests. Both functions are dependent on specific markup to work.

  • ShoppCartAjaxRequest() – javascript function that sends the AJAX request when the item is added
  • ShoppCartAjaxHandler() – javascript function that handles the AJAX response when the server responds to the AJAX request.

Each function can be overridden by your own JavaScript code. In your own JavaScript files called by your theme, you simply need to re-initialize the functions with your own code.

As of WordPress 2.8, Shopp’s scripts are loaded in the footer instead of the header to improve the perceived performance of the site. As a result, in order to override the AJAX cart functions, you will need to ensure your JavaScript file containing your custom functions are loaded in the footer after Shopp’s scripts. You can do this by using the Shopp shopp_enqueue_script() function like so:

add_action('wp', 'my_shopp_ajax_script', 99);

function my_shopp_ajax_script () {
    // important when Shopp is deactivated
    if ( function_exists('shopp_enqueue_script') ) {

        // see below for arguments
        shopp_enqueue_script('handle', 'path/to/your/script.js', array('cart'),'version',true);

    }
}

shopp_enqueue_script() Arguments

  • handle is a simple name for your script and can be anything you want to call it such as: theme-behaviors.
  • path/to/your/script.js refers to the URI to your JavaScript file.
  • cart is referenced as a dependency, ensuring your script is loaded after Shopp’s cart behaviors.
  • version should be a version number for your script such as: 1.0
  • true as the last argument will cause your script to be loaded in the footer instead of in the header.

This ensures that your script will be loaded after the default scripts so that you can override the default behavior.

ShoppCartAjaxRequest()

This is a wrapper function for making a request to the cart using AJAX. When the ajax=html or ajax=json option is added to the shopp(‘product’,’addtocart’) tag, this function is called and the appropriate default parameters are sent. The default function looks like this:

var ShoppCartAjaxRequest = function (url,data,response) {
    if (!response) response = "json";
    var $ = jqnc(),
        datatype = ((response == 'json')?'json':'html');
    $.ajax({
        type:"POST",
        url:url,
        data:data+"&response="+response,
        timeout:10000,
        dataType:datatype,
        success:function (cart) {
            ShoppCartAjaxHandler(cart,response);
        },
        error:function () { }
    });
}

ShoppCartAjaxRequest() Parameters

As shown above, the function takes 3 parameters:

  • url — the URL of the shopping cart to submit the request to
  • data — the form data to pass along in the request in query string format (name=value&name=value)
  • response — the type of response that Shopp should send back. By default this is set to json. You can also choose to get html back instead. When Shopp sends HTML updated back, it reprocesses the entire sidecart.php template and sends back the markup, updated with any changes to the cart back to the browser to replace the current side-cart element on the page (in the DOM).

By design, this function is meant to be overridable. You can replace it’s functionality by writing your own version of the function in any of your theme’s JavaScript files. For example, to override the function so that it forces Shopp to return HTML markup instead of JSON you would include something like the following in your JavaScript files:

ShoppCartAjaxRequest = function (url,data,response) {
    // force to html and set and override url
    response = "html";
    url = url = 'http://mysite.com/custom/ajaxurl.php';
    var $ = jqnc(), datatype = 'html';

    $.ajax({
        type:"POST",
        url:url,
        data:data+"&response="+response,
        timeout:10000,
        dataType:datatype,
        success:function (cart) {
            ShoppCartAjaxHandler(cart,response);
        },
        error:function () { }
    });
}

This still retains the default pass-through behavior of the original function, but now tells Shopp request from your specified URL. Alternatively, you could replace the function entirely and specify your own success function, instead of the default ShoppCartAjaxHandler() function.

ShoppCartAjaxHandler()

ShoppCartAjaxHandler() is directly responsible for taking a response from Shopp and updating the page (or more specifically, the DOM) with the new information. The default form of this function handles processing the default JSON response from Shopp then updates the sidecart to show the recently added product and updates the cart’s totals. It relies heavily on the jQuery JavaScript Framework to make light of updating the DOM. For reference, here is what the built-in handler looks like:

var ShoppCartAjaxHandler = function (cart,response) {
    var $ = jqnc(),label = '',Item=false,Totals=false,
        widget = $('.widget_shoppcartwidget div.widget-all'),
        wrapper = $('#shopp-cart-ajax'),
        ui = widget.length > 0?widget:wrapper,
        actions = ui.find('ul'),
        status = ui.find('p.status'),
        added = ui.find('div.added').empty().hide(), // clear any previous additions
        item = $('<div class="added"></div>');

    if (response == "html") return ui.html(cart);

    if (cart.Item) Item = cart.Item;
    if (cart.Totals) Totals = cart.Totals;

    if (added.length == 1) item = added;
    else item.prependTo(ui).hide();

    if (Item.option && Item.option.label && Item.option.label != '')
        label = ' ('+Item.option.label+')';

    if (Item.image)
        $('<p><img src="'+cart.imguri+cart.Item.image.id+'" alt="" width="96"  height="96" /></p>').appendTo(item);
    $('<p />').html('<strong>'+Item.name+'</strong>'+label).appendTo(item);

    $('<p />').html(asMoney(new Number(Item.unitprice))).appendTo(item);

    status.html('<a href="'+cart.url+'"><span id="shopp-sidecart-items">'+Totals.quantity+'</span> '+
                '<strong>'+$ct.items+'</strong> &mdash; <strong>'+$ct.total+'</strong> '+
                '<span id="shopp-sidecart-total">'+asMoney(new Number(Totals.total))+'</span></a>');

    if (actions.size() != 1) actions = $('<ul />').appendTo(ui);
    actions.html('<li><a href="'+cart.url+'">'+cart.label+'</a></li><li><a href="'+cart.checkouturl+'">'+cart.checkoutLabel+'</a></li>');
    item.slideDown();
}

The default response handler relies on a specific element to exist in the sidecart.php template file (used in the shopp(‘cart’,’side-cart’) Theme API call and the Shopp Cart Widget):



<div id="shopp-cart-ajax"></div>


Wherever that element is placed in the sidecart.php template file is where the recently added product will be displayed (added to the DOM). Additionally, it relies on a container element for updating the status of the total items in the cart:

<span id="shopp-cart-items"></span>

A container for the subtotal costs of the items in the cart:

<span id="shopp-cart-total"></span>

Here is an complete example of the sidecart.php content template file:



<div id="shopp-cart-ajax">
<?php if (shopp('cart','hasitems')): ?>
    <p class="status">
        <span id="shopp-sidecart-items"><?php shopp('cart','totalitems'); ?></span> <strong>Items</strong><br />
        <span id="shopp-sidecart-total" class="money"><?php shopp('cart','total'); ?></span> <strong>Total</strong>
    </p>
    <ul>
        <li><a href="<?php shopp('cart','url'); ?>">Edit shopping cart</a></li>
        <?php if (shopp('checkout','local-payment')): ?>
        <li><a href="<?php shopp('checkout','url'); ?>">Proceed to Checkout</a></li>
        <?php endif; ?>
    </ul>
<?php else: ?>
    <p class="status">Your cart is empty.</p>
<?php endif; ?>
</div>


Let’s walk through how it works.

  1. First it finds the container element for the recently added product information to go, clears any existing content out and hides it to prepare it’s display state for a slide-out animation.
  2. Next it builds an <ul> list element to layout the product information display and adds each element to the DOM. The information getting added to the product display comes from the cart object that was sent back from Shopp. It contains a large amount of updated information about the state of the shopper’s cart. The format of that object is explained in more detail later in this guide.
  3. Then it updates the total item quantity of the cart and subtotal of item costs respectively.
  4. The if statement is a simple state check. If the side-cart already has items, it simply updates the HTML of the appropriate elements. Otherwise the cart will usually have a simple message telling the shopper “Your cart is empty” and none of the updatable elements will exist, so they need to be created from scratch and added to the DOM

AJAX Cart Response Object

Below is an outline of the structural properties of the response object returned when making an AJAX Cart request:

{
    Contents: [Item, Item, Item...],    // Array of items in the cart
    Item: {                             // Object of the recently added/update cart item
        category: 1,        // ID of the category the item came from (if applicable)
        data: [],           // Array of custom product data provided by any custom product inputs in the add-to-cart form
        description: "",    // String containing the product description
        donation: {...},    // Donation settings object
        download: 1,        // Download asset id
        freeshipping: "",   // Whether the item has free shipping
        inventory: false,   // Is inventory tracked on this product
        menus: {...},       // Product variation option menus
        name: "Product",    // Name of the product
        option: {...},      // Selected variation option object
        optionlabel: "",    // Name of the variation permutation
        price: 1,           // ID of the product price record in the DB
        product: 1,         // ID of the product record in the DB
        quantity: 1,        // Number of quantity of the item in the cart
        sale: false,        // Is the product on sale
        saved: 0,           // Dollars (or other currency amount) saved if a promo price is available
        savings: 0,         // Percentage savings if a promo price is available
        shipfee: 0,         // Shipping handling fee mark-up (per unit)
        shipping: false,    // Is a shipped item
        sku: "",            // Stock Keeping Unit ID
        slug: "product",    // Product permalink slug
        tax: 0,             // Calculated tax for the item
        taxable: true,      // Is taxable
        thumbnail: {...}    // Product thumbnail image object
        total: 1.00000000,  // Total price of the cart item (unit price x quantity)
        type: "Download",   // Type of product/product variation
        unitprice: 1.0000,  // Unit price of the product
        weight: 0           // Weight of a unit of the product
    },
    Totals: {
        discount: 0,        // Discount applied to the order
        quantity: 1,        // Total quantity of products
        shipping: 1.00000,  // Total (estimated) cost of shipping
        subtotal: 1.00000,  // Total cost of all cart item totals
        tax: 0,             // Calculated tax
        taxed: 0,           // Is cart taxed
        taxrate: 0.15,      // Tax rate percentage
        total: 1.000000     // Total cost of order (subtotal+(-discount)+shipping+tax)
    }
    url: "http://wordpress/shop/cart"   // example URL of the cart
}

Handling HTML Responses

The default behavior handles JSON or HTML responses natively with the properly formatted sidecart.php. You may decide to override the default ShoppCartAjaxHandler() to pull off some custom effect.

var ShoppCartAjaxHandler = function (cart, response) {
    if ( response == 'html' )
        // set my html sidecart to all my blocks with my class
        jQuery('div.my-custom-cart-divs').html(cart);
}

Let’s say you want to include the sidecart.php template multiple times in your theme. You’ll also want to modify the sidecart.php template file. The default sidecart.php file includes the following line at the very top of the file:



<div id="shopp-cart-ajax"></div>


Instead of using the id attribute “shopp-cart-ajax”, set the class attribute to your chosen class name.



<div class="my-custom-cart-divs"></div>


Now your handler will affect all divs with the my-custom-cart-divs class.

Part of the Arsenal

AJAX is a technology that certainly has earned its lofty reputation as a powerhouse feature of the web and Shopp has tools to take full advantage of all it has to offer. The real beauty is that AJAX is a value added feature that need not be relied upon in order for the storefront to work. It’s still there in toolbox, and when used to full effect can take a storefront from ordinary to extraordinary.

Avatar of Jonathan Davis

By

Jonathan was born at an early age and began designing and developing shortly after. He is the founder of Ingenesis Limited and Project Lead on the Shopp e-commerce plugin for WordPress. He lives and works in the heart of the midwest US with his family. He fancies himself a designer of code, and is only slightly addicted to coffee.

  1. Avatar of Philip

    Hello,

    After hours and hours of searching why i couldn’t manage to get the ajax addtocart option working. I found that it is important to not override the class of the submit button like this.

    shopp('product', 'addtocart',
        // options
        array(
            'label' => 'Add to cart',
            'ajax' => 'on',
            'class' => '[override classes]'
        )
    );
    

    But to keep the class option like:

    'class' => 'addtocart ajax [override classes]'
    

    I think you should mention this in the MANUAL, because that would save loads and loads of time

    July 16th   #

  2. Avatar of wbickley

    It’s also worth noting here that it is essential the form surrounding the input has a “product” class on it, otherwise this functionality will not work.

    August 20th   #

  3. Avatar of wbickley

    I have also found that the url which is supplied to the ShoppCartAjaxRequest appears to be the form object rather than the action url, I would advise changing “url: url” to “url: $(url).attr(‘action’)”.

    Feel free to merge these comments.

    August 20th   #

  4. Avatar of wbickley

    Final comment I promise.

    It is also worth mentioning that there are events that get fired during the entire process which can be used to add extra functionality to the ajax behaviour.

    shopp_cart_ajax_request – Called before the ajax request is made
    shopp_cart_ajax_success – Called if an ajax response was received
    shopp_cart_ajax_item – Called during the ShoppCartAjaxHandler()
    shopp_cart_ajax_successful – Called if the ShoppCartAjaxHandler() was successful

    For example, the following will create an alert when the adding was successful:

    $(‘form.product’).on(‘shopp_cart_ajax_successful’, function(){
    alert(‘Added product to bag.’);
    });

    August 20th   #

You must be logged in to post a comment.

© Ingenesis Limited. Shopp™ is a registered trademark of Ingenesis Limited.

Skip to toolbar