Blog

Magento 1: JS Scopes in Checkout

Inline JS within one-page checkout isn't executed in the global scope and that can break things.

In Magento 1, the one-page checkout module renders the page request, but checkout steps are loaded via AJAX from these files:

/app/design/frontend/*/*/template/checkout/onepage/*.phtml

AJAX responses are usually just data and the request handler contains logic to use it. Magento’s checkout is weird because the PHTML files generating the response contain inline JS. You might think the <script> tag is just injected, but it’s not. Check the markup (the code is missing). It’s executed by eval(), which changes the scope (see MDN).

Most JS will work without issues, but a function defined in the PHTML returned by AJAX will not be global.

How this breaks

Context: a site’s checkout has multiple fulfillment methods. Users can specify a date/time, but only for in-store pickup. The date/time inputs must be hidden when other methods are selected.

<input type="radio" value="pickup" onclick="toggleDatePicker('show');">
<input type="radio" value="fedex"  onclick="toggleDatePicker('hide');">

<script>
    function toggleDatePicker (state) {
        // Something
    }
</script>

Normally this will work because the function has global scope. But this template is returned by AJAX, so the inline JS is evaluated in the scope of the caller (thanks ECMAScript quirks). In one-page checkout, the caller is a method inside opcheckout.js.

When users change the fulfillment method, toggleDatePicker() fails because it’s undefined in the global scope. If you console.log('frodo') in the step’s template, then Chrome DevTools will show the source as VM#### not the document index. Different scope. Voilà.

JS patterns in Magento 1 are from 2007, so fussing with scope and inline code is purely coping with old practices.

My fix

I moved toggleDatePicker() to opcheckout.js since that file contains other customizations on this project. The scope is global so the AJAX-injected onclick handler can access it properly.

Alternative fix

You could move toggleDatePicker() to the parent template checkout/onepage.phtml which isn’t rendered by AJAX, but keeping the JS customizations together makes more sense.

Alan is smart

Reference

Discourse Gravitated