36

Does anyone know how to have all of Magento's JS script tags e.g. <script type="text/javascript" src="http://sitename.com/js/prototype/prototype.js"></script> render out before the closing </body>?

I've tried this once before, but I was given an error which I think was along the lines of the addJS method not being available where I used it, possibly in reference footer.

6
  • 4
    Take GTMetix and YSlow's recommendation with a pinch of salt. The time you spend investing in this will not derive a noticeable increase in performance and almost certainly would be better spent profiling your code (stripping the fat) or simply finding a better/faster host. Commented Jan 30, 2013 at 16:33
  • 1
    @sonassi While a valid point, taking half an hour to implement JMax's answer is not going to harm anything and is part of a selection of front-end performance enhancements that can be included into a base theme.
    – Glo
    Commented Jan 31, 2013 at 14:00
  • 1
    @Glo We're all for performance enhancements. But the "benefit" this will yield simply doesn't exist. The first page load is the only time the JS will be loaded and thereafter served from the browser cache. Blocking on initial load is much more likely to be slow PHP generation than JS delivery; and even still, the server transit or users last-mile connectivity will play a bigger part in how long it takes to actually pull down. Commented Jan 31, 2013 at 20:24
  • 1
    @sonassi That's like saying that you should ignore first visit users because they'll have a primed cache soon. Of course there are PHP enhancements that can and should be undertaken but, as a front-ender, it's my job to worry about how I can not only speed up asset delivery but also perceived load time for the user and loading scripts at the bottom of the page to prevent blocking rendering is part of that strategy. Modern browsers can handle scripts without blocking but you know as well as I that milliseconds == ££ in ecommerce. Of course, this is generally a moot point with script loaders.
    – Glo
    Commented Feb 1, 2013 at 8:52
  • I'm with @Glo, it's a small piece of efficiency tuning, and might not make a night and day difference, but it shouldn't be ignored. Getting content on screen quickly for a first-time visitor is important.
    – STW
    Commented May 22, 2014 at 21:51

7 Answers 7

23

It depends on your request. For instance, lastly, I had been removed all Prototype scripts from the Homepage of the Magento store which I didn't face any problem. But as I said, it depends on your theme, extensions etc.

To moving the script :

Find the following line in page.xml of your theme

<block type="core/text_list" name="before_body_end" as="before_body_end" translate="label">

And insert the following just before :

<block type="page/html_head" name="jsfooter" as="jsfooter" template="page/html/jsfooter.phtml">
   <action method="addJs"><script>your_script.js</script></action>
</block>

For Magento 1.9 use this:

<block type="page/html_head" name="jsfooter" as="jsfooter" template="page/html/jsfooter.phtml">
       <action method="addItem"><type>skin_js</type><name>js/yourskinfile.js</name><params/></action>
    </block>

Create the template file in app/design/frontend/[package]/[theme]/template/page/html/jsfooter.phtml and put the following

<?php echo $this->getCssJsHtml() ?>

Add below in your template just before closing </body> tag.

<?php echo $this->getChildHtml('jsfooter') ?>
3
  • are you sure core/text_list has an addJs() method?
    – Alex
    Commented Jan 30, 2013 at 18:49
  • you are right @Alex, I had a mistaken, sorry about that! Commented Jan 30, 2013 at 20:23
  • Cheers man, this works. What I was actually expecting to have to say after getting this working is that when you have JS merging running, it negates this anyway because the merged file then gets appended to the head, but actually, what Magento seems to have done in my test is create two merged files, one for each page/html_head block and the merged file for the footer js is appended before '</body>'. That's potentially useful. I've had some issues with JS merging and jQuery which was down to not including jQuery.noConflict. Now I have JS merging working the js before '</body>' might be useful. Commented Mar 5, 2013 at 14:51
21

There are two problems with moving the tag. The greatest problem is that for some reason Magento injects a lot of JS that is dependent on prototype directly into the <body/> tag. Moving the scripts to the end of the document (though good for load times), will break a lot of the pages in Magento.

The other problem is actually doing it. There doesn't seem to be <move /> tag, or similar functionality. What I have done for custom scripts that I have created is adding scripts like this. It is more redundant, but it works:

<block type="page/html_head" name="foot.scripts" template="page/template/foot-scripts.phtml">
    <action method="addJs"><script>jmax/global-min.js</script></action>
</block>
2
  • Marked @Oğuz Çelikdemir correct because he'd supplied the more detailed answer which works, but you basically gave the same answer so thanks a lot it does work. Commented Mar 5, 2013 at 14:45
  • 2
    For some reason is that you are using a javascript framework, in fact several. They must be resident early on for proper page rendering. There are reasons to include certain javascripts before body end. If you don't understand this and are blindly shoving it all to the bottom, you are engaging in cargo cult programming. Setting up a little shed with a dummy radio and headsets is done a lot with Magento and sometimes by people who are supposed to be experienced web developers. Some stuff belongs exactly where it was placed. Commented May 26, 2013 at 16:46
15

In Magento 1.x this is a fool's errand. There are simply too many inline scripts littered throughout the template files in Magento that will break if you relocated the core JS files from the <head>. Potentially in Magento 2, this situation will change but it's straddling Prototype and jQuery as Magento migrates away from Prototype.

For other scripts, you should place them before the </body> element. I've found it helpful to ignore Magento's <action method="addJS|addItem"> XML and simply create a new template file for each script, which includes a plain HTML script reference like:

<script src="<?php echo $this->getSkinUrl('js/hobbiton.js'); ?>"></script>

Then you can embed this template file anywhere (and still use before/after to control the order) like so:

<block type="core/template name="jquery.hobbiton" after="-" template="custom/jquery/hobbiton.phtml" />
6

Moving external JavaScript to the bottom is not enough in most cases. If you use any templates with inline JavaScript, like in the default themes, you will need to delay the execution of these until all dependencies (prototype.js, varien.js, ...) are loaded.

An approach is to extract all inline <script> elements from rendered blocks using an observer for http_response_send_before and move them to the end right after the external scripts. While you are at it, you can move all script elements, not only inline. This saves you the hassle of moving them via the layout model, which was clearly not intended by Magento.

Tom Robertshaw created an extension that does exactly this, with a single observer that changes the response HTML using regular expressions: https://github.com/bobbyshaw/magento-footer-js

He uses the core_block_abstract_to_html_after event but only takes action if the current block is the root block. This means, the observer is called more often, but it should leverage block caching to some degree.

1
  • The Robertshaw extension works really well throughout the site, except on the payment panel of the OPC cart. Totally blanks the panel. I think it's the Payment Gateway validation script. It moves everything to the bottom just before the closing </body> tag. Commented Feb 9, 2016 at 19:24
2

I highly recommend the mediarox pagespeed module to help you optimise your javascript (and css) and improve google pagespeed insight ranking.

It works by parsing html output by Magento and then performing a cut and paste action on the code to move javascript to the bottom of the html code. The process is fast but is best used in conjunction with a full page cache to cache the html changes.

More information on how this module works and can help you to improve pagespeed rank here:

http://blog.gaiterjones.com/magento-google-pagespeed-jscsshtmlminify-optimisation/

-1

For Magento v1.6+ (need to test in older versions);

1 - create an template file in page/html/footer/extras.phtml with this content:

<?php echo $this->getCssJsHtml() ?>

2 - Add this html node to your layout xml:

<reference name="before_body_end">
<block type="page/html_head" name="extra_js" as="extraJs" after="-" template="page/html/footer/extras.phtml">
    <action method="addItem"><type>skin_js</type><name>js/jquery.min.js</name></action>
</block>

3 - That is it!

-2

Due to an issue with this other script (in product/list.phtml) <script type="text/javascript"> decorateList('category-list', 'none-recursive') </script> I had to move some JS at the end of my page.

I wasn't able to make what's indicated above work so I find another way to achieve :

I override the Mage/page/Block/Html/Footer.php controller by recreating it with the same path in app/local folder.

Here is the full path to be created if not exists :

app/local/Mage/page/Block/Html/Footer.php

In this file, I add functions from Head.php which can be found in the same folder of Magento's core (i.e. Mage/page/Block/Html/Head.php).

Functions you need to make it works are (obviously the full function, here I just indicate the name to remain concise):

public function addItem($type, $name, $params=null, $if=null, $cond=null)
{...}

public function addJs($name, $params = "")
{...}

public function getCssJsHtml()
{...}

protected function &_prepareStaticAndSkinElements($format, array $staticItems, array $skinItems, $mergeCallback = null)
{...}

protected function _separateOtherHtmlHeadElements(&$lines, $itemIf, $itemType, $itemParams, $itemName, $itemThe)
{...}

protected function _prepareOtherHtmlHeadElements($items)
{...}

Then I add to my custom (the one in my theme) page/html/footer.phtml the call for this :

<?php echo $this->getCssJsHtml() ?>

At the end, I can now add JS in my footer by calling it in any layout by

<action method="addJs"><script>yourscript.js</script></action>
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.