Including JS & CSS in templates

Permalink 1 user found helpful
What is the proper method to include js & css in block templates.

IE each template might use separate css or js includes.

 
aaron replied on at Permalink Reply
So I can just add this to the view?

$html = Loader::helper('html');
$css = $html->css('css/my_style_sheet.css');
$this->addHeaderItem($css);
$js = $html->css('js/my_javascript.js');
$this->addHeaderItem($js);
Remo replied on at Permalink Reply
Remo
you can add such code to your template. You'd have to add it to your theme but a theme doesn't have to be related to a block. At least not in every case..

It's therefore better to include it with your block..

I wrote a tutorial that describes such a template here:http://www.codeblog.ch/2009/04/concrete5-drop-down-menu/3/...

Please not that you need at least 5.3 to use this feature!
powermick replied on at Permalink Reply
powermick
The preceding discussion is the:
http://www.concrete5.org/index.php?cID=11162...
powermick replied on at Permalink Reply
powermick
I work with version 5.3.1.1.

Indeed over the discussions, I think the solution has been affected.

First, sayings the problem :
When adding a custom template for a block, you sometimes need to add a block:
- An appearance (with a css file)
- Events or features (for one or more JavaScript files)

One solution would be to include these files in a type page of the Template of the page.

But why to load a file in a page which not containing this block?

So this is the Controller of the block that have to do this addition.
Now comes the problem of the inclusion of these files.

How it is managed now?

is actually the file concrete / elements / header_required.php which calls the function outputHeaderItems to print all the tags and css js inclusion in the property of the object headerItems View.

/** 
       * Function responsible for outputting header items
       * @access private
       */
      public function outputHeaderItems() {
         $a1 = (is_array($this->headerItems['CORE'])) ? $this->headerItems['CORE'] : array();
         $a2 = (is_array($this->headerItems['VIEW'])) ? $this->headerItems['VIEW'] : array();
         $a3 = (is_array($this->headerItems['CONTROLLER'])) ? $this->headerItems['CONTROLLER'] : array();
         $items = array_merge($a1, $a2, $a3);
         $items = array_unique($items);
         foreach($items as $hi) {
            print $hi . "\n";
         }
      }




This property is an array has three elements: 'Core', 'VIEW', 'controller'
Each of these elements contains a tag for including css or js file.
These tags are added to these tables by the function of the object View: addHeaderItem

/** 
       * Function responsible for adding header items within the context of a view.
       * @access private
       */
      public function addHeaderItem($item, $namespace = 'VIEW') {
         $this->headerItems[$namespace][] = $item;
      }


This function has been re-implemented in the Controller class which is of course the grandfather of class Class Controller of our block.

/** 
    * Adds an item to the view's header. This item will then be automatically printed out in the <head> section of the page
    * @param string $item
    * @return void
    */
   public function addHeaderItem($item) { 
      $v = View::getInstance();
      $v->addHeaderItem($item, 'CONTROLLER');
   }


The problem of the order of inclusion is only managed by the membership in one of three elements: 'Core', 'VIEW' or 'controller'

not to enter into conflict with the include of concrete5 obviously addHeaderItem function at the class level Controller includes tags inclusion file at the 'controller', at the lowest level.


As you've probably seen in outputHeaderItems a duplication of the call was not to include twice the same file:

$items = array_unique($items);


But it work only if the tag inclusion is strictly identical.

Hence the problem of defining whether the block needs a resource is a comprehensive site or block specific.

If the resource is global to the site (including jQuery, for example), the solution of Aaron is correct, there must therefore include this feature in our Controller class block:

public function on_page_view() {
           $html = Loader::helper('html');
            $js = $html->javascript('jquery.js');
            $this->addHeaderItem($js);
        }


If the resource is specific to the block (including a jQuery plugin for example) should therefore include this function within the Controller class that our block will look in the js file block:

public function on_page_view() {
            $html = Loader::helper('html');
            $b = $this->getBlockObject();
            $bv = new BlockView();
            $bv->setBlockObject($b);
            $jsPath =  $bv->getBlockPath().'/jqueryPlugin.js';
            if(file_exists($jsPath)) {
               $jsTag = $html->javascript($bv->getBlockURL().'/jqueryPlugin.js');
               $this->addHeaderItem($jsTag);           
            } 
        }


And if the resource is specific to the template of the block (eg the inclusion of a CSS Template for this block), this piece of code include the css file the name of the Template block located in the Tools of block:

public function on_page_view() {
            $html = Loader::helper('html');
            $b = $this->getBlockObject();
            $bv = new BlockView();
            $bv->setBlockObject($b);
            $templateName=$b->getBlockFilename();
            if(!empty($templateName)){
                $templateName=str_replace('.php','',$templateName);
                $cssPath= $bv->getBlockPath().'/tools/'.$templateName.'.css';
                if(file_exists($cssPath)) {
                   $cssTag = $html->css($bv->getBlockURL().'/tools/'.$templateName.'.css');
                   $this->addHeaderItem($cssTag);           
                }
            }            
        }


Good to know: a mechanism for automatic inclusion in the file Head is already in place at the block: the file view.css and view.js, but this only works for blocks set in concrete / blocks /
Fernandos replied on at Permalink Reply
Fernandos
LoL I use BASE_URL for such tasks :D
quick n dirty but works ^

<script type="text/javascript" href="<?php BASE_URL.'/themes/mytheme/js/myscript.js'?>">
TheRealSean replied on at Permalink Reply
TheRealSean
Sorry to bring back an old thread but does anyone know how this could be applied to Blocks appearing from within the Scrapbook?

I have got a couple to work by embedding the style into the view.php of a template but when it appears in the scrapbook, it is often affected by the default dashboard styles of concrete5 and ignores any style in the view.css

Its hard to determine, the difference of 5 navigation displays when all look the same and only differ by name
jjaeger replied on at Permalink Reply
jjaeger
I haven't really looked into it but I am almost certain that the blocks from the scrapbook are the same blocks that are installed on your backend, available for use anywhere on your site.

That being said, I believe the same solution mentioned above is valid.
jordanlev replied on at Permalink Reply
jordanlev
Seanom, looks like you've already found this, but in case anyone is looking here in the future...
This is a bug with the system:
http://www.concrete5.org/developers/bugs/5-4-1-1/global-scrapbook-b...

Hopefully it will be fixed some day.
mnakalay replied on at Permalink Reply
mnakalay
Hello,

I tried powermick's solution after just modifying the code a bit. I added the function to the controller.php of the block I'm working with:
public function on_page_view() {
            $html = Loader::helper('html');
            $b = $this->getBlockObject();
            $bv = new BlockView();
            $bv->setBlockObject($b);
            $cssPath =  $bv->getBlockPath().'/view.css';
            echo '<xmp>';
            print_r('cssPath='.$cssPath);
            echo '</xmp>';
            if(file_exists($cssPath)) {
            $cssTag = '<link rel="stylesheet" media="screen" type="text/css" href="'.$bv->getBlockURL().'/view.css">';
            echo '<xmp>';
            print_r('cssTag='.$cssTag);
            echo '</xmp>';
            $this->addHeaderItem($cssTag);

My cssPath and cssTag get printed on the screen with correct values so I know the code is executed and does what it's supposed to do but nothing is added to the header of the page.

I should add that I am using the block from the global scrapbook so it appears on all my pages.

Any idea what I'm doing wrong?

Many thanks
jordanlev replied on at Permalink Reply
jordanlev
It's this line:
if(file_exists($cssPath)) {

...the file_exists() function is expecting a path in your server's file system, but the $cssPath is a URL. Remove that "if" statement (and its corresponding closing brace) and it should work.
mnakalay replied on at Permalink Reply
mnakalay
Hello,

Thank you for your fast answer.

I am going to try your idea but are you sure that's the problem?

As I said, my cssTag gets printed correctly on the screen and the print_r responsible for that is inside that if(file_exists($cssPath))

I assume then the if is working and everything in it is executed correctly except the part responsible for adding the line in the header.

I have edited my previous message to explain that I'm using the block from within the global scrapbook so it appears on every page. Maybe that has to do with it?

Many thanks again for your help, I will try your solution anyway and let you know.
mnakalay replied on at Permalink Reply
mnakalay
Hi again jordanlev,

I tried your solution but still the same problem.

Thanks anyway
jordanlev replied on at Permalink Reply
jordanlev
Can you post your updated code? Also can you post what it is exactly that both the $cssPath and the $cssTag variables are outputting?
mnakalay replied on at Permalink Reply
mnakalay
Here it is, without the if
$html = Loader::helper('html');
$b = $this->getBlockObject();
$bv = new BlockView();
$bv->setBlockObject($b);
$cssPath =  $bv->getBlockPath().'/view.css';
echo '<xmp>';
print_r('cssPath='.$cssPath);
echo '</xmp>';
$cssTag = '<link rel="stylesheet" media="screen" type="text/css" href="'.$bv->getBlockURL().'/view.css">';
echo '<xmp>';
print_r('cssTag='.$cssTag);
echo '</xmp>';
$this->addHeaderItem($cssTag);


and I get this printed on the screen just above the block:

cssPath=J:/xampp/htdocs/philip_concrete/packages/defunct_social_icons/blocks/defunct_social_icons/view.css

cssTag=<link rel="stylesheet" media="screen" type="text/css" href="/philip_concrete/packages/defunct_social_icons/blocks/defunct_social_icons/view.css">
jordanlev replied on at Permalink Reply
jordanlev
I see. Yeah, that looks right. Where exactly do you have this code? If it's not in the block controller's on_page_view() function than it won't get into the page header. Also, it may just be the problem that others have reported above with global scrapbooks.
jordanlev replied on at Permalink Reply
jordanlev
By the way, is this a custom template for an existing block? If so, then the getBlockPath() function is returning the path to the block, not the path to where your custom template lives (if for example it's a built-in C5 block and your custom template is in your site's top-level "blocks" directory, the block path would be concrete/blocks/block_handle/, not blocks/block_handle/templates/whatever/ ).
mnakalay replied on at Permalink Reply
mnakalay
Well actually something here is not too clear about where to put the code. Here's why:
First I am not working with a custom template.
Second, there is a function view() but no function on_page_view()

when I put my code in view(), although nothing gets added to the header, at least the rest works and the paths appear on screen. But if I create a on_page_view() function, then what I put in it has no effect whatsoever on my page.

The reason I'm here is that, as others have said above, when you put a block in the scrapbook then It doesn't pull the css or js files correctly. First I solved the problem by having a copy of my css at the root and hardcoding its path in my header.css. It worked but it sounds really bad, especially having a copy of that css file where it doesn't belong. Then I read about addHeaderItem and I thought it was a much more elegant solution that would allow me to keep the css file where it belongs and just have the block provide the path to the header. That's the solution offered in this thread that I just copied. But it doesn't work for me and I don't know why.
jordanlev replied on at Permalink Reply
jordanlev
addHeaderItem doesn't work when called from view() because by the time the view() function is called, all header items have already been sent. So you need to put the code in the on_page_view() function. It's okay if it's not there already, just add it like this:
public function on_page_view() {
  //put the code here
}


Sometimes when you echo things in certain places from C5, they don't get outputted to the screen -- this very well could be true for the on_page_view() function so don't let that keep you from using it.

If you're not using a custom template (which I'm assuming means you've created your own block here), then you shouldn't have any problems with the scrapbook rendering. (But who knows, maybe there's some new bug with it.)
mnakalay replied on at Permalink Reply
mnakalay
I am using a block called social icons by defunct. I got it from the marketplace.

I definitely think it is a bug. I was just hoping there was a workaround.

Thanks for your patience