Magento and jQuery

Warnings, Disclaimers, Fear, Loathing, etc.

The javascript described herein isn’t production hardened. The methods described herein for getting the javascript into magento proved effective for me on 1.5.0.1. The code example at the end was meant to prove the concept of an Ajax Add To Cart Solution that didn’t require eleventy million hours of work. You have been warned. Also, much of the code here was adapted from the very good entry at the TheUnical Technologies’ Blog, so a big Thank You to them!

The Magento jQuery Problem

Magento (up through v1.5.0.1) uses the ProtoType and Scriptaculous javascript libraries. These libraries help make the nice JavaScript effects/ elements that are built-in to Magento, especially on the administrative side.

For development on a Magento Site, though, we have a few problems. Problem one is that Magento is behind the curve on current versions of these libraries. I am not going to spend days on end figuring out exactly where the default magento installation uses these libraries. A cursory examination of the frontend default theme indicates about 185 “javascript” uses (as indicated simply by grep’ing for them). I doubt that upgrading these javascript libraries is high on Magento’s to-do list. And I don’t blame them: what’s there now works, in both their frontend default, and in the admin panel.

Problem two is that all the designers/ developers we run into only know jQuery. I’m not going to get into a debate about it. jQuery works, we understand it, so fine, go ahead and use it. Whether it’s jQuery or not is beside the point, really. I don’t want to rely on Magento’s plans for User Experience anyway, so I don’t want to be “piggy-backing” on their choice of javascript libraries.

As of 1.5.0.1, Magento reads in the layout/page.xml file of your theme, and brings in the various javascript libraries and – for lack of a better term – “munges” (not minimizes!) them into a single JavaScript file. This makes a degree of a sense as it cuts down the number of http requests required to bring all the necessary JavaScript files down to the user’s browser.

Magento cannot (yet?) bring in JavaScript (or anything else, for that matter) from a Content Delivery Network (CDN). At present, Magento does not support CDNs in its Community Edition platform (This isn’t to say Enterprise Edition can: it’s just to say I only know the Community Edition, and it can’t).

So bottom line: I want to use jQuery/ jQuery UI to enhance the user experience on my Magento site. I want to use jQuery’s ThemeRoller to define and standardize the CSS I’m using. And I want my jQuery to load in in its minimized form from a CDN.

Magento Configuration

1. Load in the jQuery libraries.

Option 1: Load in the jQuery libraries from a CDN

I’m using Google here. jQuery’s site lists other options you may wish to consider. In the admin panel, go to System->Configuration->Design->HTML Head, and scroll down to the Miscellaneous Scripts box. Add these lines:

<script
  src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"
    type="text/javascript">
</script>
<script 
  src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"
    type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
    var $jQ = jQuery.noConflict();
//]]>
</script>

This accomplishes the magic of having a global $jQ as the jQuery library – in no conflict mode so it won’t interfere with anything the “default” javascript libraries might want to do.  (Remember also that jQuery UI is stateful. That could prove handy!). Now you’re ready to use jQuery wherever you want to. You will of course need the CSS, though, so, on to step 2.

Option 2: Load in the jQuery libraries locally.

First, we’ll need to download the jquery.min.js and jquery-ui.min.js libraries into our js folder. In this example, I have a “mxwest” folder under js where my JavaScript resides.

Next, I specify that these files should be loaded in my layout/page.xml file:

<action method="addJs"><script>mxwest/jquery.min.js</script></action>
<action method="addJs"><script>mxwest/jquery-ui.min.js</script></action>

Finally, I instantiate $jQ in my theme’s template/page/head.phtml file. In my case, I placed the code (in blue) after getTranslatorScript but  before getIncludes. I’ve no idea if this matters or not, but it does work on my installation.

<?php echo $this->getCssJsHtml() ?>
<?php echo $this->helper('core/js')->getTranslatorScript() ?>
<script type="text/javascript">
//<![CDATA[
    var $jQ = jQuery.noConflict();
//]]>
</script>
<?php echo $this->getIncludes() ?>

2. Load in your jQuery CSS

I placed the jQuery UI theme (as opposed to the Magento theme) into my skin, in the css folder. The theme generated by jQuery’s ThemeRoller comes contained in its own subfolder. I simply ftp’d that into place in my skin.

Next, I updated my layout/page.xml file to bring the jQuery UI CSS in:

<action method="addCss">
    <stylesheet>css/custom-theme/jquery-ui-1.8.10.custom.css</stylesheet>
</action>

jQuery UI’s ThemeRoller named everything for me. So I just went with it.

Sample Code

Here’s an example using $jQ to create a little “working” dialog and perform an Ajax add to cart. This is way faster than waiting for the whole checkout page to be returned to the user. This code appears in my theme’s view/addtocart.phtml.

[Note: this code really belongs folded into a minimized JavaScript library of our own creation, but calm down I’m not solving everything in the whole world at once. Also, it should be refactored. Later.]

<div id="dialog" title="Adding to Cart"></div>  <!-- “ui-widget” is jQuery UI standard! -->
<script type="text/javascript">
// This function is called when the Add to Cart button is pressed:
function
addItemToCart() {
    $jQ("#dialog").dialog('destroy'); // Clobber anything that might be left around
    $jQ("#dialog").html("Working..."); // Set my message.
    $jQ("#dialog").dialog(); // Show it...
 
    // Notice a little php here. Prolly should be an argument to this function.
    var sku = "<?=$_myProductSku?>";
 
    /* Same here. The reason for concatening sku to qty is in anticipation of making this function
        usable on a list page - I need unique identifiers for valid XHTML. */
    var qty = $jQ("#qty<?=$_myProductSku?>").val();
 
    // This is simply the GET query to my add to cart program.
    var cart_parms = "sku=" + sku + "&qty=" + qty;
 
    /* mxwest_atc.php is a mini-Mage app that adds to the cart. The Mage:: object ensures this is portable
        to other domain names. */
    var myUrl = "<?=Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);?>/mxwest_atc.php?"+cart_parms;
 
    /* This is a jQuery 1.5 implementation! Notice how my callbacks are defined!
        Successful or not, notice that I inject jQuery UI styling into the dialog
        using the addClass() method. */
    var jqhxr = $jQ.ajax({ url: myUrl })
        .success( function(){
        $jQ("#dialog").dialog('destroy');
        $jQ("#dialog").html("<span class=\"ui-icon ui-icon-info\"></span>Successfully added "
            + sku + " to cart.");
 
        $jQ("#dialog").addClass("ui-state-highlight ui-corner-all");
        $jQ("#dialog").dialog({
                buttons: {
                    "Ok": function() {
                    $jQ(this).dialog("close");
                    }
                }
            });
        })
        .error( function() {
        $jQ("#dialog").dialog('destroy');
        $jQ("#dialog").html("<span class=\"ui-icon ui-icon-alert\"></span>Sorry, something bad happened. "
            + sku + " not added.");
 
        $jQ("#dialog").addClass("ui-state-error ui-corner-all");
        $jQ("#dialog").dialog({
                buttons: {
                    "Ok": function() {
                    $jQ(this).dialog("close");
                }
        });
    })
    .complete( function() {});
}

Facebook comments: