2

I'm working on adding a simple feature to a single Joomla article. Editing the article itself is necessary in this case, I can't modify the template files (or add the JS to the entire site).

In the article editor (as per the doc), my article is as follows ...

    {source}
    <?php
        $doc = JFactory::getDocument();
        $doc->addStyleSheet('mycode/css/article_1.css');
        $doc->addScript('mycode/js/article_1.js');
    ?>
    {/source}

    <div id="article_1"></div>

When viewing the page, both CSS and JS are loaded, but addScript() is first-come-first-served, so my script (which contains jQuery) loads before jQuery. The console spits out:

Uncaught TypeError: $ is not a function

I tried changing the addScript line to ...

    $doc->addCustomTag('<script src="mycode/js/article_1.js" defer></script>');

... (note the defer tag) but that doesn't seem to do anything (the script isn't added). I saw somewhere that I could use ...

    $doc->addScript('mycode/js/article_1.js', 'text/javascript" defer="false');

... but the editor (both TinyMCE and CodeMirror) encodes the " above (to & quot;) which breaks that possibility.

I've also tried wrapping the article_1.js code in an event listener ...

    document.addEventListener('DOMContentLoaded', function(){ ... }

... but that doesn't seem to help :\ I still get:

Uncaught TypeError: $ is not a function

How do you add a jQuery script from within an article?

1

Try this workaround below, by calling jquery.framework from the article. It should override jquery.framework call that comes from your template so then jquery will be called before your script.

{source}
<?php
    JHtml::_('jquery.framework');
    $doc = JFactory::getDocument();
    $doc->addStyleSheet('mycode/css/article_1.css');
    $doc->addScript('mycode/js/article_1.js');
?>
{/source}

<div id="article_1"></div>
  • It doesn't really override the call in the template. It just sets its place in line. The call to addScript puts that script in an array, and the array is processed first-in-first-out when the page is rendered. Since the article is processed before the template, anything added by the article gets rendered before the template adds do. By calling jquery.framework from the article like you did, you set it to render ahead of the article js. Meanwhile, when the template calls addScript later in the process, the system sees jQuery is already in the array, and skips duplicating it. – Arlen Nov 15 at 22:05
  • @Arlen - in the above code in the first line the JHtml class loader method is loading the whole jQuery framework in an article and in the above code nothing is checking whether the jQuery framework is already loaded or not (at least I do not see where it is checked). This can be later a source of conflicts if a module, plugin, component or the template itself is loading the same or other versions of jQuery libraries... – Zollie Nov 16 at 11:56
  • @Zollie I don't think you need to check whether jQuery framework is already loaded or not in this case, because as Arlen said, if the system sees jQuery is already in the array, it skips duplicating it. – webchun Nov 17 at 2:27
  • @webchun - no, unfortunately it is not true according to my experience (at least I should have seen such a checking in Joomla, but I have not). Smarter extension developers usually build in these checks when they load these frameworks. Arlen was writing about addScript method which is part of $doc object, however here you load jQuery framework via JHtml object’s class loading method. That is the second reason why I pointed to this issue above. But if you could point to the method where the jQuery presence is checked in Joomla system the way you described, that would enlighten me on this. – Zollie Nov 17 at 3:49
  • @Zollie isn't it JHtml::_('jquery.framework'); is a common way to call jquery framework in joomla templates? Even in protostar this code is written literally like that, not preceded by checking block. I also usually use JHtml::_('jquery.framework'); when I create a module that need jquery, and so far I have never see any jquery call duplication in my projects. – webchun Nov 17 at 12:12
1

It’s a jQuery conflict (more like noConflict issue). You should start and wrap your script for example like:

jQuery.noConflict();
(function( $ ) { // if you want to use $
  $(function() {
    // More code using $ as alias to jQuery
  });
})(jQuery);

Or redefine jQuery like:

var $j = jQuery.noConflict();
// $j is now an alias to the jQuery function;       creating the new alias is optional.

$j(document).ready(function() {
    $j( "div" ).hide();
});

And with few other methods. These will release the conflict on the page. https://learn.jquery.com/using-jquery-core/avoid-conflicts-other-libraries/

Update:

It is my fault or laziness that I have not given a short additional explanation about my answer.

First, the original question is two questions actually: 1. How to add scripts to articles? 2. What does the given error mean? And these are a bit misleading because it could get confusing answers.

On the question how to load scripts into articles the answer is relatively easy since there is addScript and some other obvious methods. And addScript() was already in the question so there was no problem with that. If you do not mistype your code then these will work.

The second question which is a bit more important in this question and it is what does this error mean?

Uncaught TypeError: $ is not a function

Now, this error is most of the time appears when jQuery framework is loaded in noConflict mode as usually is the case in Joomla. And if jQuery is loaded in noConflict mode then the $ shortcut will not be available, thus the error appears, when you try to run your scripts with that shortcut. That is why I gave my answer as it is, as a good practice you should wrap your jQuery scripts as it is above. And that is also explaining why you should not load the whole jQuery framework again within an article (since most probably it is already loaded so that is not the problem and loading it into an article could cause other complications later).

  • 1
    I've just tried adding the scripts through a straight up <script src="blah.js" defer> in the code, and wrapping the scripts in the functions as you suggest (immediately after the DOMContentLoaded event listener) ... and it seems to have pushed me in the right direction ... Thank you for that, I'll mark your answer correct after a bit of testing. – neokio Nov 15 at 5:46
  • 1
    I'm just a bit astonished that there's no $doc->addScriptToEnd() or $doc->addStyleSheetToEnd() function. Seriously, how are articles supposed to call unique, page-specific code?? – neokio Nov 15 at 5:52
  • 1
    Thank you for the help! While the your solution requires every script to be within that wrapper, it definitely works. I'm marking @webchun's answer as correct, however, because it also allows me to include off-site (CDN served) jQuery scripts (which also works using the <script ... defer> method), and because it's such a clean one-line solution. – neokio Nov 15 at 17:12
  • 1
    @neokio - no problem. My solution is handling totally different problem and question than webchuns’s solution since I do not consider a good practice if you load a complete jQuery library within an article (that will cause conflicts later on the pages). The jQuery library should be loaded globally on the site (maybe inside your default site template not within an article) and small custom scripts should be used/wrapped and then loaded as I gave it in my answer to avoid jQuery conflicts later. That would be the point of this subject. So loading the base jQuery library within an article, well... – Zollie Nov 15 at 17:55
  • 1
    I would never suggest that. – Zollie Nov 15 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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