Categories


Archives


Recent Posts


Categories


Magento 1.9, RWD, and the Checkout class

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

I’m still digging into the new Magento 1.9 CE responsive web design (RWD) theme, but one thing I noticed immediately (since it conflicted with my new Custom Checkout Step extension) is how the theme needed to replace a method in the Prototype JS Checkout class.

#File: skin/frontend/rwd/default/js/opcheckout_rwd.js
Checkout.prototype.gotoSection = function (section, reloadProgressBlock) {
    // Adds class so that the page can be styled to only show the "Checkout Method" step
    if ((this.currentStep == 'login' || this.currentStep == 'billing') && section == 'billing') {
        $j('body').addClass('opc-has-progressed-from-login');
    }

    if (reloadProgressBlock) {
        this.reloadProgressBlock(this.currentStep);
    }
    this.currentStep = section;
    var sectionElement = $('opc-' + section);
    sectionElement.addClassName('allow');
    this.accordion.openSection('opc-' + section);

    // Scroll viewport to top of checkout steps for smaller viewports
    if (Modernizr.mq('(max-width: ' + bp.xsmall + 'px)')) {
        $j('html,body').animate({scrollTop: $j('#checkoutSteps').offset().top}, 800);
    }

    if (!reloadProgressBlock) {
        this.resetPreviousSteps();
    }
}

This little bit of javascript changed the definition of the gotoSection method of the Prototype JS Checkout class. It looks like the Llamas, Falkowski, and/or anyone else involved wanted to add some animation effects as you progressed through each step. Normally this method looks like this

#File: skin/frontend/base/default/js/opcheckout.js
gotoSection: function (section, reloadProgressBlock) {

    if (reloadProgressBlock) {
        this.reloadProgressBlock(this.currentStep);
    }
    this.currentStep = section;
    var sectionElement = $('opc-' + section);
    sectionElement.addClassName('allow');
    this.accordion.openSection('opc-' + section);
    if(!reloadProgressBlock) {
        this.resetPreviousSteps();
    }
},

This is a completely valid technique — the Prototype JS class system was designed to allow you to do things like this. There are, however, two potential problems here.

The first is, the original gotoSection method never gets called when you use this technique. This means repeating the original code in your new method. This is similar to creating a Magento rewrite without calling parent::methodName. When I needed to do something similar for Custom Checkout Step, I used a technique like this

Checkout.prototype.namespaceOriginalGotoSection = Checkout.prototype.gotoSection;
Checkout.prototype.gotoSection = function(section,reloadProgressBlock)
{
    //... custom code here ...   
    this.namespaceOriginalGotoSection(section,reloadProgressBlock);
};     

Here we’ve used javascript’s flexible object system/first class functions to store a copy of the gotoSection method, replace it, and then call the original method after our new method has done its stuff. This technique is a bit more bulletproof, and ensures that any changes made to the gotoSection method in the future will be picked up by our modification.

In fact, because I used this technique in Custom Checkout Step, my extension was mostly compatible with the new responsive theme in Magento 1.9. Where it wasn’t compatible brings me to the second problem with the new RWD theme’s gotoStep method: hard coded assumptions. Specifically, this conditional

#File: skin/frontend/rwd/default/js/opcheckout_rwd.js
if ((this.currentStep == 'login' || this.currentStep == 'billing') && section == 'billing') {
    $j('body').addClass('opc-has-progressed-from-login');
}

This bit of code adds the class opc-has-progressed-from-login to the document’s <body/> tag, and is meant to signal when/if the user has progressed on from the Checkout Method step. It’s a hook for CSS animation effects. The problem here is the conditional

if (this.currentStep == 'login' || this.currentStep == 'billing') && section == 'billing'

In english this says “If the current step is login or billing, AND the section we’re moving to is billing”. The problem with this is it assumes no one has added any steps to the checkout. When a Custom Checkout Step extension user has their step immediately after the “Checkout Method” step, it means the new $j('body').addClass('opc-has-progressed-from-login'); never gets called. Or, it never got called until I released the 1.0.1 version of the extension.

While this is a valid critique of the RWD reference template, it’s pretty understandable how/why it happened. The Magento One Page Checkout really wasn’t designed to be modified like this — and ironically the checkout is one of the most modified systems in Magento. There’s always going to be conflicts. A big part of my planning for the Custom Checkout Step extension was budgeting enough time for the inevitable support requests.

So, with all deference given to different programming styles and tradeoffs, my approach here would have been slightly different. First, with the conditional, I would have tried one of the following

if (section != 'login')
...
if ((this.currentStep == 'login' || this.currentStep == 'billing') && section != 'login')

Namely, if you’re going somewhere that’s not the login, my gut says it’s safe to assume that the one page checkout has proceeded from the login. Instead of checking the specific cases that exists now, we’re checking for all cases that might exist in the future.

Second, I’d have broken the new animation functionality lines out into their own methods

Checkout.prototype.gotoSection = function (section, reloadProgressBlock) {
    // Adds class so that the page can be styled to only show the "Checkout Method" step
    this.flagHasProgressedFromLogin();

    if (reloadProgressBlock) {
        this.reloadProgressBlock(this.currentStep);
    }
    this.currentStep = section;
    var sectionElement = $('opc-' + section);
    sectionElement.addClassName('allow');
    this.accordion.openSection('opc-' + section);

    // Scroll viewport to top of checkout steps for smaller viewports
    this.fixViewportScroll();

    if (!reloadProgressBlock) {
        this.resetPreviousSteps();
    }
};

Checkout.prototype.flagHasProgressedFromLogin = function() {
    if ((this.currentStep == 'login' || this.currentStep == 'billing') && section == 'billing') {
        $j('body').addClass('opc-has-progressed-from-login');
    }
};

Checkout.prototype.fixViewportScroll = function() {
    if (Modernizr.mq('(max-width: ' + bp.xsmall + 'px)')) {
        $j('html,body').animate({scrollTop: $j('#checkoutSteps').offset().top}, 800);
    }    
};

By adding new flagHasProgressedFromLogin and fixViewportScroll methods to the Checkout class, we’ve made it much easier for a third party developer to change any of the logic they need to without interfering with the core behavior of the gotoSection method.

All in all though, regardless of these small points of high nerdery, it’s going to be nice working in a modern frontend framework in an officially supported way.

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 18th May 2014