Webseiten und ZubehörProgrammierung UmsetzungPflege Backups BetreuungJoomla-Spezialist

JoomlaSchnipsel

Kürzlich bin ich auf eine Forums-Antwort des befreundeten Re:Later gestoßen, mit Code wie man in Joomla unerwünschte JavaScript-Links entfernen kann, damit sie nicht im HEAD des Templates geladen werden. Joomla sammelt diese erst in diversen Erweiterungen zusammen, bevor sie in den Quelltext der Webseite eingesetzt werden. Sein Code ist nicht ganz rund.

In den meisten Fällen wird er zwar klappen, aber leider nicht immer.

Re:Later arbeitet mit

$headnow = $this->getHeadData();

womit ein Array aus Arrays der gesammelten HEAD-Daten abgeholt wird, darunter auch die einzubindenden JavaScript-Dateien, gesammelt im Sub-Array $headnow['scripts']. Danach entfernt er einige der Dateien aus diesem Sub-Array und schreibt sie wieder zurück in die HEAD-Daten

$this->setHeadData($headnow);

Problem

Beim Tippsen eines absichtlich sehr früh eingreifenden System-Plugins (noch nicht so viel geladen in den Head-Datas) fiel mir auf, das klappt nicht verlässlich mit get und setHeadData..

Ist das Sub-Array $headnow['scripts'] nach dem Aufräumen ein leeres Array, wurde also alles daraus entfernt, verweigert Joomla das Rückschreiben und belässt die Head-Datas wie vor dem Putzen. Siehe Methode public function setHeadData($data) in Datei /libraries/document/html/html.php

$this->_scripts = (isset($data['scripts']) && !empty($data['scripts'])) ? $data['scripts'] : $this->_scripts;

Dort steht !empty(...) anstatt is_array(...), was ich sinnvoller fände. Ein leeres Array wird abgewiesen, geht nicht durch und die Head-Datas des Document werden nicht aktualisiert.

Lösung

Anstatt obige Methoden zu verwenden greifen wir direkt auf das _scripts-Array des Document zu.

In einem Template

(in Anlehnung an Re:Laters Code, besser die Korrektur seines Codes, das er vor dem <jdoc:include type="head" /> im Template reinschob)

Das Document ist in einem Template gleich $this, also bearbeiten wir das Array $this->_scripts direkt. Das ist inhaltlich identisch zu obigem $headnow['scripts'],  kommt aber ohne empty-Prüfung durch.

<?php
// Welche zu löschen/entfernen? src ENDET(!) auf:
$erase = array(
 '/mootools-core.js',
 '/mootools-more.js',
 '/bootstrap.min.js',
 '/jquery.min.js',
);

// Nur die Javascripte.
foreach ($this->_scripts  as $scr => $i){
 foreach ($erase as $er)
 {
  $offset = strlen($scr) - strlen($er);
  if (strpos($scr, $er, $offset) !== false)
  {
   unset($this->_scripts[$scr]);
  }
 }
}
?>

Beachte: Sollte es Plugins geben, die mit der Methode onBeforeCompileHead versuchen, die ggf. entfernten Scripte erneut zu laden, kann es sein, muss nicht, dass sie doch wieder auftauchen. Hängt z.B. davon ab, ob das Plugin JHtml-Methoden dafür verwendet oder irgendwie anders rumwurstelt.

Killt man mit einem

$this->_scripts = array();

Im Template alle zu ladenden JavaScript-Dateien, dann lädt bspw. das lazyload-Plugin von Viktor Vogel trotzdem

<script src="/plugins/system/lazyloadforjoomla/assets/js/lazyloadforjoomla-jquery.js"></script>

Da dieses Script wiederum zwingend JQuery benötigt, was wir aber mit unserer Radikalmethode entfernt haben und das Plugin korrekt versucht ebenfalls zu laden, aber nicht darf (egal, warum. Antwort gerne auf Rückfrage.), wird es zu einem JavaScript-Fehler kommen. Hilft nur, das Plugin zu deaktivieren.

(Das Lazyload von Kubik Rubik wurstelt nicht! Damit es keine Missverständnisse gibt...)

D.h., dass wir in einem Template zu spät dran sind, den HEAD zu bereinigen, wenn man solcherlei "Konfliktdateien" ebenfalls raushaben möchte.

Mittels Plugin

onAfterRender

Nur hier kann das Säubern des HEAD mit wahrscheinlich größtem denkbaren Erfolg gelingen, wenn das Plugin an eine Stelle sortiert wird, wo andere Plugins bereits ähnliche Arbeit in diesem Plugin-Event beendet haben. Auch an unsauber geladene Dateien (manche Templates sind ja krass unterwegs), kommt man dran. Das ist allerdings aufwendiges Gefiesel. Man muss mit recht komplexen, regulären Ausdrücken arbeiten, preg_replace usw. Ein Blick in das Plugin JQuery Easy, das den Head eines bereits gerenderten HTML-Quelltextes aufarbeitet, aufräumt und ähnliches, zeigt ... viel viel Code. Gehe ich hier nicht näher darauf ein.

onBeforeCompileHead

Auch hier ist entscheidend, dass das Plugin seine Arbeit als letztes tut, damit andere Plugins bereits fertig sind, die die Methode ebenfalls zum Laden weiterer Skripte verwenden. Im Idealfall prüft das Plugin bei der Installation bzw. beim ersten oder jedem Aufruf im Backend seine Position (ordering) und sortiert sich in der Datenbanktabelle #__extensions nach hinten, falls nötig, damit es dann im Frontend als letztes ausgeführt wird.

Der Unterschied zu obigem Code ist, dass wir statt

$this->_scripts

wie folgt auf das Array zugreifen

$doc = JFactory::getDocument();

indem wir folgend mit

$doc->_scripts

weitermachen, mit identischem Code wie oben.

Ein unsinniger, fiktiver Code, mit dem man nahezu alle HEAD-Daten via System-Plugin killen könnte, nur damit man mal alle in Frage kommenden Arrays gesehen hat (Seh gerade, fehlt noch was...Sind nicht alle...)

public function onBeforeCompileHead()
{
 if (!JFactory::getApplication()->isAdmin())
 {
  $doc = JFactory::getDocument();
  $doc->_styleSheets = array();
  $doc->_scripts = array();
  $doc->_script = array();
  $doc->_links = array();
  $doc->_style = array();
  $doc->_custom = array();
  $doc->_metaTags = array();
 }
}
JavaScript PHP Joomla Plugin Template