2

Let's say my extension makes use of a popular JQuery plugin. I can include the Javascript file in my extension and link to it using JHtml::script or another method.

The trouble is that for all I know, another extension may be loading the exact same Javascript library, just at another URL. These will need to two HTTP requests instead of one unnecessarily.

1 Answer 1

6

In Joomla, you can add scripts using JHtml::script or $doc->addScript, and if you add the same path twice, it will only get included once in the output <head>:

JHtml::script("path/to/jquery.scrollTo.min.js");

This will not deduplicate the same script at different paths. Unfortunately, there is no built-in way to do this that I can see. For JQuery itself, you can use the shipped PHP library:

JHTML::_('jquery.framework');

Hacky way server-side:

You can use this hacky way. It simply checks all the scripts that have been added already, and only adds the new path if none of the existing scripts match a regex:

if (! isScriptAdded('/^jquery\.scrollTo\b.*\.js$/')) {
    JHtml::script("path/tojquery.scrollTo.min.js");                                                                                   
}

function isScriptAdded($scriptRegex) {
    $document = JFactory::getDocument();
    $headData = $document->getHeadData(); # JHtml::script and $document->addScript add to this
    foreach ($headData['scripts'] as $script) {
        if (preg_match($scriptRegex, basename($script))) {
            return true;
        }   
    }   
    return false;
}

Hacky way client-side:

You could load the library in Javascript, setting a global variable before you do so. For example, this is Facebook's Javascript SDK v2.3 code snippet:

window.fbAsyncInit = function() {
  FB.init({
    appId      : 'your-app-id',
    xfbml      : true,
    version    : 'v2.3'
  });
};

(function(d, s, id){
   var js, fjs = d.getElementsByTagName(s)[0];
   if (d.getElementById(id)) {return;}
   js = d.createElement(s); js.id = id;
   js.src = "//connect.facebook.net/en_US/sdk.js";
   fjs.parentNode.insertBefore(js, fjs);
 }(document, 'script', 'facebook-jssdk'));

The first part sets a global variable, window.fbAsyncInit, and the second part actually loads the external script by dynamically appending a new <script> tag. If you wanted to make it idempotent, you could just surround it with this if statement:

if (! window.fbAsyncInit) {
    // Facebook snippet here
    // ...
}

This is hacky, because you're hoping that everybody else is loading this library in the same way and setting the global variable correctly (that is, before loading and executing the library). In this case, you're hoping especially that the appId and version params don't change. Also, it would theoretically be better if the server could send just one snippet, instead of multiple snippets that know how to detect each other.

What about using a CDN?

Some popular Javascript libraries are on well known CDNs. For example, Google offers a CDN to host Web Font Loader and other libraries. The hope is that the user agent will have the library in its cache even before it visits your website for the first time. If two extensions both use the same CDN, then the library does not need to be downloaded twice. The main downside of using a CDN is that your website now depends on a third party, which is why I would recommend not taking this approach by default with extensions that are going to be shared online.

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.