Adding Javascript, CSS, or metatags to the head tag from a Joomla extension

Official Documentation: https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page

From anywhere after the application has been initiated and before the document has been rendered you can add scripts, stylesheets and inline styles into the head tag.

// The basic techniques

// grab a reference to the JDocument from the Factory
$document = JFactory::getDocument();

// add a link to a stylesheet
$document->addStyleSheet($url);

// add a script tag with a src to a url
$document->addScript($url);

// add a script tag with some js in it
$document->addScriptDeclaration('
    window.event("domready", function() {
        alert("An inline JavaScript Declaration");
    });
');

// add a style tag with some styles in it
$style = 'body {'
        . 'background: #00ff00;'
        . 'color: rgb(0,0,255);'
        . '}'; 
$document->addStyleDeclaration($style);

// add arbitrary whatever you want 
$document->addCustomTag('<meta property="og:image" content="https://example.com/images/example.jpg">');

Adding scripts and styles using a system plugin

If you wish to add different css and js to the front and back of the site then you should use a system plugin and test for which client it is like this:

    class plgSystemRandomname extends JPlugin
    {
       function onBeforeCompileHead()
       {
           $doc = JFactory::getDocument();
           $app = JFactory::getApplication();

           // prior to J3.7 use isSite() instead
           if ($app->isClient('site')){

             // add all yr scripts and stylesheets from the frontend, eg.
             $doc->addScript('/path/to/script.js');

           } elseif ($app->isClient('administrator')) {

             // add all yr js and css for the backend, eg.
             $doc->addScript('/path/to/different_script.js');

          }
       }
    }

Here is the documentation for making a plugin: https://docs.joomla.org/J3.x:Creating_a_Plugin_for_Joomla

Understanding the order in which scripts load

Here is the Joomla lifecycle.

  1. Bootstrap Framework & Application
  2. Route
  3. Component
  4. Modules
  5. Template
  6. Render and Respond

Calling addScript, addStylesheet etc. adds your script to the appropriate array in the jdocument. The order the scripts are added is the order they are rendered in the template, usually by this call:

<jdoc:include type="head" />

Custom head tags are loaded after the scripts so if you want to load a script after all the other scripts you can use:

$doc->addCustomTag('<script src="/media/com_example/js/quite-nice-script.js"></script>');

For advanced usage you can reach into the document directly and reorder, remove and add scripts or stylesheets probably in system plugin in the onBeforeRender() method.

Further reading: https://github.com/joomla/joomla-cms/blob/staging/libraries/src/Document/HtmlDocument.php

Adding anything to anywhere the DIY way

Before Joomla responds it fires an onAfterRender event where the buffer now contains the html payload as a string. Hence you can use a system plugin to manipulate the html however you want. Below is a rough example which inserts some script tags before the closing body tag.

<?php 
defined('_JEXEC') or die('Restricted access');

class plgSystemScriptbeforebody extends JPlugin {

  public function onAfterRender()
  {
      $app = JFactory::getApplication();

      // only insert the script in the frontend
      if ($app->isClient('site')) {

          // retrieve all the response as an html string
          $html = $app->getBody();

          // replace the closing body tag with your scripts appending to the closing body tag
          $scripts = [
              '/media/com_example/js/example1.js',
              '/media/com_example/js/example2.js',
          ];

          $tags = "";
          foreach($scripts as $s){
              $tags .= '<script src="' . $s . '"></script>';
          }

          $html = str_replace('</body>',$tags . '</body>',$html);

          // override the original response
          $app->setBody($html);
        }
    }
}