6

Hello Joomla Community,

How can a css class be appended to the <body> from inside a system plugin?

Any advice is appreciated, Thank you.

  • Maybe editing the template directly is easier? – Bakual Jun 1 '15 at 6:20
  • Thank you for the advice, I got it running already. Editing the template could perhaps also be an option, but because I was already forced to write a plugin, I assumed that adding a css class in the plugin is the easiest option. This way neither the template nor any other third party component gets altered. – SePröbläm Jun 1 '15 at 11:38
6

If you want to inject code in the html code (adding class in body tag) using OnAfterRender event:

$content= JFactory::getApplication()->getBody();
     /* your modification of $content here */
if (preg_match('/(<body.*?)(class *= *"|\')(.*)("|\')(.*>)/', $content)) {
  $content = preg_replace('/(<body.*?)(class *= *"|\')(.*)("|\')(.*>)/', '$1$2$3 new_class$4$5', $content);
  } elseif (preg_match('/(<body.*?)(>)/', $content)) {
    $content = preg_replace('/(<body.*?)(>)/', '$1 class ="new_class">', $content);
  }
JFactory::getApplication()->setBody($content);

Do not use JResponse::getBody() since it is deprecated as of Joomla 3.2

  • Thank you very much! How can I append a new css class to $content then and ensure it's appended to <body>? – SePröbläm May 31 '15 at 21:11
  • Edited. Actually regex should check for class and add additional ones like class="first_class second_class third_class" – pl71 May 31 '15 at 21:46
  • Wow, that are some seriously advanced regexes. Thank you very much for sharing this! – SePröbläm Jun 1 '15 at 11:11
1

Using regular expressions to manipulate valid HTML is not advisable because regex is "DOM-unaware" meaning that it doesn't know if it is actually modifying a tag or just text that seems like a tag. Also, unless expressly told to replace only once, it will happily modify multiple matching strings (effectively damaging your content). Finally, using DomDocument is a superior technique because it will intelligently/reliably parse and modify a class attribute regardless of the position of the attribute in the tag and regardless of the character used to quote the value (single or double quotes).

Code: (PHP Demo)

$new_class = "new_class_attr";

$body = JFactory::getApplication()->getBody();

$dom = new DOMDocument; 
$dom->loadHTML($body, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$bodytag = $dom->getElementsByTagName('body')->item(0);
if ($bodytag->hasAttribute("class")) {
    $new_class = $bodytag->getAttribute("class") . " " . $new_class;
}
$bodytag->setAttribute("class", $new_class);
JFactory::getApplication()->setBody($dom->saveHTML());

And just in case anyone mistakes my endorsement of DOMDocument for fear of regular expressions, here is how I would code it up using refined regex calls.

Code: (PHP Demo) (First Regex Demo) (Second Regex Demo)

$new_class = "new_class_attr";

$body = JFactory::getApplication()->getBody();

$body = preg_replace('~<body[^>]*class *= *["\']\K~', "$new_class ", $body, 1, $count);  // add to existing class attribute
if (!$count) {
    $body = preg_replace('~<body\K~', " class=\"$new_class\"", $body, 1);  // create class with attribute
}
JFactory::getApplication()->setBody($body);

But, again, I don't recommend regex for this job because it will be less stable... Imagine if you had an attribute (and didn't realize it) like specialclass or data-class. So look, let's not go down the tit-for-tat rabbit hole of creating a body string that will break the pattern then adjust the pattern to suit the vulnerability -- just use a DOM parser.

  • Why wouldn't using JavaScript be a better solution? – Eoin Jan 2 at 21:12
  • 1
    The point that I am making is not about client-side versus server-side. This is about regex versus dom parsing. Using regex on valid html is more likely to lead to unintended results compared to a dom parser. The OP is writing a plugin so I agree with pl71 that onAfterRender() seems a good trigger to call upon. – mickmackusa Jan 2 at 23:12
  • 1
    Your js technique looks to be reliable (so long as your vistors are allowing js -- and all reasonable users will be). joomla.stackexchange.com/q/23989/12352 So long as the javascript is executed after the body tag is rendered, this should work for you. – mickmackusa Jan 3 at 4:17
  • 1
    Yeah but addScriotDeclaration is loaded in the head. So I can't use that. I personally think it's annoying that Joomla! doesn't support before the body out of the box. Actually... If I use defer it probably does. – Eoin Jan 3 at 4:19
0

I thought about this and I came up with a different solution. Assuming your plugin has a field called "class" in the XML.

$app = JFactory::getApplication();
$appParams = $app->getParams();

$class = "";
if ($this->params['class'])
{
    $class = $this->params['class'];
}

$pageClass = $appParams['pageclass_sfx'];
if(!empty($pageClass)) 
{
    $class = " " . $this->params['class'];
}
else 
{
    $class = $this->params['class'];
}
$pageClass = $pageClass . $class;
$app->setParams($pageClass);

As the menu/page class is already added to the body we can just concatenate ur new class

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.