Adam Moss
Adam Moss

Reputation: 5712

Reorder Magento JavaScript Includes (addJs)

I'll keep this simple.... on my product pages I need to remove the prototype.js file and replace it with the latest version of prototype. So far using local.xml I have successfully replaced it using this:

<action method="removeItem"><type>js</type><name>prototype/prototype.js</name></action>
<action method="addJs"><script>prototype/prototype-new.js</script></action>

The issue is that now prototype is loaded below everything other includes which stops it working.

Is there a way of setting the order of a JavaScript include using local.xml without having to remove and add every single file again?

Upvotes: 8

Views: 17186

Answers (7)

Dmitri Sologoubenko
Dmitri Sologoubenko

Reputation: 2909

You can use the params element, with "data-group" HTML attribute in it, Magento will put items with "params" set after any items with no "params" set. You can use local.xml in your theme to ensure the order of these elements (I recommend to let Magento add its standard files first):

<action method="addJs">
    <script>prototype/javascript0.js</script>
    <params><![CDATA[data-group="js002"]]></params>
    <!-- creates first group js002 -->
</action>
<action method="addJs">
    <script>prototype/javascript1.js</script>
    <params><![CDATA[data-group="js002"]]></params>
    <!-- appends to created group js002, after javascript0 -->
</action>
<action method="addJs">
    <script>prototype/javascript2.js</script>
    <params><![CDATA[data-group="js001"]]></params>
    <!-- creates first group js001 -->
</action>
<action method="addJs">
    <script>prototype/javascript3.js</script>
    <params><![CDATA[data-group="js001"]]></params>
    <!-- appends to created group js001, after javascript2 -->
</action>
<action method="addJs">
    <script>prototype/javascript4.js</script>
    <params><![CDATA[data-group="js002"]]></params>
    <!-- appends to created group js002, after javascript1 -->
</action>
<action method="addJs">
    <script>prototype/javascript5.js</script>
    <!-- no params supplied, will be rendered before any groups -->
</action>

The order in which elements with "params" set are rendered in only determined by the order in which actions are executed. Items are rendered grouped by the value of "params", so once group "js001" is defined by the first action (javascript2.js), successive actions with the same group name will append to that group, etc.

In the example above, the order of rendering will be:

  • prototype/javascript5.js (no params, comes first)
  • prototype/javascript0.js (first created params group "js002")
  • prototype/javascript1.js (first created params group "js002")
  • prototype/javascript4.js (first created params group "js002")
  • prototype/javascript2.js (second created params group "js001")
  • prototype/javascript3.js (second created params group "js001")

Items added using addItem addJs, addCss, etc, are threated in the same way, however they differ (and will be super-grouped accordingly) by type (see order below), with each group in super-groups internally ordered as explained above:

  • js
  • skin_js
  • js_css
  • skin_css

Upvotes: 45

Hassan Ali Shahzad
Hassan Ali Shahzad

Reputation: 2724

jquery conflict with Prototype in magento:

Simplest solution is to add at the end of your main jquery file.

jQuery.noConflict();

Or create your custom js file and add this line and also include this file after main js.

Upvotes: 0

Eric Seastrand
Eric Seastrand

Reputation: 2633

I think that Barny Shergold's solution is the most correct, but the comment about avoiding changing core files is correct. To have the best of both worlds, you can implement Barny's solution in a module that extends the Mage_Page_Block_Html_Head class like so:

Yourcompany/Layouthelper/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Yourcompany_Layouthelper>
            <version>0.0.1</version>
        </Yourcompany_Layouthelper>
    </modules>
    <global>
        <blocks>
            <page>
                <rewrite>
                    <html_head>Yourcompany_Layouthelper_Block_Html_Head</html_head>
                </rewrite>
            </page>
        </blocks>
    </global>
</config>

Yourcompany/Layouthelper/Block/Html/Head.php

class Yourcompany_Layouthelper_Block_Html_Head extends Mage_Page_Block_Html_Head {

    public function replaceItem($type, $name, $replace) {
        $newArray = array();

        foreach($this->_data['items'] as $key => $value) {
            if($key == $type.'/'.$name) {
                $newArray[$type . '/'. $replace] = array(
                'type'   => $type,
                'name'   => $replace,
                'params' => $value['params'],
                'if'     => $value['if'],
                'cond'   => $value['cond']);
            } else {
                $newArray[$key] = $value;
            }
        }
        $this->_data['items'] = $newArray;
        return $this;
    }
}

Upvotes: 0

Ken Koch
Ken Koch

Reputation: 426

I realize this post is pretty old but i stumbled on it while researching this problem, so i figured why not.

Not sure if this is the most elegant solution, but its working sufficiently well for us.

I basically created a new block in my local.xml like this: <block type="core/template" name="prehead" template="page/html/prehead/main.phtml" />

Then just modified my main layouts (1column.phtml and friends) to contain this: <head> <?php echo $this->getBlockHtml('prehead') ?> <?php echo $this->getChildHtml('head') ?> </head>

In main.phtml I add my JS that I want to load first

<script type='text/javascript' src='<?php echo $this->getSkinUrl('js/require.js', array('_secure'=>true)); ?>'></script> <script type='text/javascript' src='<?php echo $this->getSkinUrl('js/main.js', array('_secure'=>true)); ?>'></script>

This is convenient for us as well because it still allows us to change the main.js depending on what page we are on by doing something like this in local.xml:

<checkout_onestep_index translate="label"> <label>One Step Checkout</label> <block type="core/template" name="prehead" template="page/html/prehead/checkout.phtml" /> </checkout_onestep_index>

Hope it helps someone who happens to stumble on this.

-Ken

Upvotes: 1

Barny Shergold
Barny Shergold

Reputation: 11

Ok here's my solution. First copy app\code\core\Mage\Page\Block\Html\head.php to app\code\local\Mage\Page\Block\Html\head.php

Then add this function to the file :

public function replaceItem($type, $name, $replace)
{
    $newArray = array();

    foreach($this->_data['items'] as $key => $value) {
        if($key == $type.'/'.$name) {
            $newArray[$type . '/'. $replace] = array(
            'type'   => $type,
            'name'   => $replace,
            'params' => $value['params'],
            'if'     => $value['if'],
            'cond'   => $value['cond']);
        } else {
            $newArray[$key] = $value;
        }
    }
    $this->_data['items'] = $newArray;
    return $this;
}

Now in your .XML layout file include a line like this :

<action method="replaceItem"><type>js</type><name>prototype/prototype.js</name><replace>onestepcheckout/prototype/prototype.js</replace></action>

The parameters are exactly as for removeItem BUT now the replace file will be injected into the list in the same place as the original file. It will also inherit the same param, if and cond values. Please use this code carefully as I've only just written and done basic tests on it!

Upvotes: 0

MagePsycho
MagePsycho

Reputation: 2004

Since local.xml is loaded at last by the magento system, so the xml mentioned there are merged at last. That may be the reason that your new prototype is loaded below other scripts.

Alternatively, you can override the Mage_Page_Block_Html_Head::getCssJsHtml() method and play with it.

Thanks

Upvotes: 0

Anton S
Anton S

Reputation: 12750

Mage_Page_Block_Html_Head $_data; holds the array of those items so I guess you can filter it for your own good. Default methods does not allow defining order of added items.

Upvotes: 1

Related Questions