After telling dozens other extension developers how to correctly load your own version of jQuery in Joomla, we got this week into a conflict with a big community extension. They just load their own copy without care. So it's time to write a blog post how to do it correctly. A side note. In this post we provide very simplified code examples, they should give you a starting point. Please read the Joomla docs carefully and also the Joomla core code itself, that you know what you are doing.
How to load jQuery
The Joomla CMS comes with it's own unmodified jQuery script in version v1.12.4. When loaded, it executes it in no conflict mode together with jQuery migrate to be compatible with older versions. So every extension which needs jQuery in Joomla MUST load it with the following PHP code. This ensures that the right jQuery script is loaded only once.
JHtml::_('jquery.framework');
More documentation can be found in the official docs.
The Problem
When you are loading in your extension a jQuery plugin, then you do it mostly that way:
JHtml::_('jquery.framework'); JHtml::_('script', 'com_foo/myJqueryPlugin.js', array('version' => 'auto', 'relative' => true)); JFactory::getDocument()->addScriptDeclaration("jQuery(document).ready(function() { jQuery('#selector').myJqueryPlugin(); })");
In Joomla are all Javascript files loaded from all extensions before the inline Javascript code. When after your extension is executed another one is loading it's own copy of jQuery like:
JHtml::_('script', 'com_bar/jquery-3.2.1.min.js', array('version' => 'auto', 'relative' => true));
Then the head will look like
<head> <script src="/media/jui/js/jquery.js?56690c624209a47cb536df0eac0a28f3"></script> <script src="/media/jui/js/jquery-noconflict.js?56690c624209a47cb536df0eac0a28f3"></script> <script src="/media/jui/js/jquery-migrate.js?56690c624209a47cb536df0eac0a28f3"></script> <script src="/com_foo/js/myJqueryPlugin.js"></script> <script src="/com_bar/js/jquery-3.2.1.min.js"></script> jQuery(document).ready(function() { jQuery('#selector').myJqueryPlugin(); }) </head>
This would result in the Javascript error "Function myJqueryPlugin not found". The problem is that the second load of jQuery overwrites the first global jQuery object where the plugin myJqueryPlugin is attached to. So all plugins do not work anymore which are attached to the old jQuery object.
The solution
So why this strange way of loading jQuery trough JHtml? It offers the possibility to overload that function in a plugin. What you have to do is to create a Joomla plugin according to the docs for the group system. Then add a function onAfterInitialise and register your own jquery service like in the following code snippet:
public function onAfterInitialise() { JHtml::register('jquery.framework', function ($noConflict = true, $debug = null, $migrate = true) { JHtml::_('script', 'com_bar/jquery-3.2.1.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); // Check if we are loading in noConflict if ($noConflict) { JHtml::_('script', 'jui/jquery-noconflict.js', array('version' => 'auto', 'relative' => true)); } // Check if we are loading Migrate if ($migrate) { JHtml::_('script', 'jui/jquery-migrate.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); } }); }
This ensures that only your jQuery script is loaded. It reduces the page size as not two jQuery scripts are loaded and it will not break all other jQuery based extensions. The head of your page will then look like:
<head> <script src="/com_bar/js/jquery-3.2.1.min.js"></script> <script src="/media/jui/js/jquery-noconflict.js?56690c624209a47cb536df0eac0a28f3"></script> <script src="/media/jui/js/jquery-migrate.js?56690c624209a47cb536df0eac0a28f3"></script> <script src="/com_foo/js/myJqueryPlugin.js"></script> jQuery(document).ready(function() { jQuery('#selector').myJqueryPlugin(); }) </head>
So we at Digital Peak hope that all extension developers are on the same page and the jQuery errors are history. Time no to turn vanilla :-)