TinyButStrong - the PHP Template Engine
Categories > TinyButStrong general >

Managing CSS & JS files

By: doubi
Date: 2011-11-19
Time: 03:19

Managing CSS & JS files

Hi all,

I want to use a sub-template for a certain UI component which might appear in different places on a range of my pages.

The UI component requires certain CSS and JS files to operate.

I want to Do The Right Thing with CSS and JS, which as far as I know is to a) combine as many as possible b) minify as much as possible and maybe c) put them at the end of my markup so the browser doesn't have to wait for them to load before displaying content.

So, if I've got various different UI components, as well as different headers and sidebars, which all require their own special CSS and JS to function correctly, what's the best way for me to manage them so that the final markup is as small and well-organised as possible?

One particular problem I've seen on the site I'm working on is different components loading the same CSS file twice.

I imagine this question could apply to any templating system. I've had the same question when working with full-blown frameworks in Perl, but never thought of a good solution.

Many thanks!
By: doubi
Date: 2011-11-19
Time: 07:32

Re: Managing CSS & JS files

Ok, after a little more research I think the above can be boiled down into one question:

Can I do template inheritance with TBS?

I haven't seen anything in the documentation, but I find it a little hard to follow sometimes :-)

By all means let me know if this is the wrong question to ask.
By: Skrol29
Date: 2011-11-19
Time: 22:41

Re: Managing CSS & JS files

Hi Doubi,

TBS does sub-templates which is the good way to insert UI components in a main HTML template.
Such Components can be pieces of HTML beginning with <style> and <script>.
Like
<style>...</style>
<script>...</script>
<div>...</div>

Element <script> can be placed outside the <header> for both HTML5 and XHTML.
Nevertheless, element <style> can be placed outside the <header> only in HTML5 (attribute scoped="scoped" is then welcome). It is valid in XHTML even if all browsers do support this.

If you take a look at the TBS parameter "getbody", you see that it can extract several part of a sub-template. Typically, if you template is a valid HTML page like
<html>
  <header>
    <script></script>
    <style></style>
  </header>
  <body>
     XXX
  </body>
</html>
the it can be extracted and inserted in the main template as:
    <script></script>
    <style></style>
     XXX
The purpose if this feature is to build valid HTML sub-template witch will be inserted in a main template as a component.
By: doubi
Date: 2011-11-19
Time: 23:44

Re: Managing CSS & JS files

Hi Skrol29,

Thanks for your reply. I hadn't realised the significance of being able to specify any set of tags with getbody, thanks for the tip.

Out of interest, how does that work internally? Does TBS hold the template as a DOM at some point or is it a lighter, simpler trick done with regexes?

What this doesn't quite solve is being able to collect all css / js assets from all subtemplates together, to be able to combine & optimize them.

I've got a concept for a workaround...
<html>
  <!-- topLevelTemplate.html -->
  <head>
    [onshow.~MyAssets.output]
  </head>
  <body>
    [onload;file=component1.html]
    [onload;file=component2.html]
  </body>
</html>

<!-- component1.html -->

[onload.~MyAssets.add('/css/com1.css')]
[onload.~MyAssets.add('/js/com1.js')]
<div>
  Check out my cool function!<br />
  <a href="#" onclick="groovy_function_from_com1_js()">Click here!</a>
</div>
<!-- ... and similar for component2.html --->

// the PHP file

my $assets = new MyAssetManager;
$TBS->ObjectRef['MyAssets'] =& $assets;

// as subtemplates are loaded, asset manager is populated
$TBS->LoadTemplate('topLevelTemplate.html');

// now prepare assets
$assets->concatenate();
$assets->minify();

// $assets->output is called
$TBS->Show();

... what do you think? Is it sensible? And more importantly, am I able to quote-delimit arguments for methods called from template fields, e.g. ~MyAssets.add('/css/com1.css') ?

I might as well say that I've been looking at Assetic, and I'm jealous :-)
http://www.slideshare.net/kriswallsmith/introducing-assetic-asset-management-for-php-53

Thanks!
By: Skrol29
Date: 2011-11-21
Time: 23:01

Re: Managing CSS & JS files

Hi,

> Out of interest, how does that work internally?
> Does TBS hold the template as a DOM at some point or is it a lighter, simpler trick done with regexes?

No regexp, no DOM. TBS simply searches opening and closing tags of the HTML/XML elements.

> ... what do you think? Is it sensible? And more importantly, am I able to quote-delimit arguments for methods
> called from template fields, e.g. ~MyAssets.add('/css/com1.css') ?

I'm afraid the quote-delimiting is not possible. PHP coding at the HTML side is not possible and is not the philosophy.
But you can do a similar feature using parameter "onformat":

<!-- component1.html -->

[onload;onformat=~MyAssets.add;asset='/css/com1.css']
[onload;onformat=~MyAssets.add;asset='/js/com1.js']

<div>
  Check out my cool function!<br />
  <a href="#" onclick="groovy_function_from_com1_js()">Click here!</a>
</div>
<!-- ... and similar for component2.html --->

Tha the PHP side, method MyAssets->add() must have the "onformat" syntax, and then it can read parameter "asset".
By: doubi
Date: 2011-11-22
Time: 14:56

Re: Managing CSS & JS files

Hi Skrol29,

Thanks for the clarifications.

> I'm afraid the quote-delimiting is not possible.

Ah, I misread the tbs_class.php source then. I had thought that the argument \' passed to f_Loc_PrmRead by meth_Locator_FindTbs on line 851 was used to allow ' as a delimiter. Clearly I need to pay more attention to what's happening in f_Loc_PrmRead.

> PHP coding at the HTML side is not possible and is not the philosophy.
> But you can do a similar feature using parameter "onformat":

It wasn't my intention to use PHP in the template; I thought I was following the syntax described at http://tinybutstrong.com/manual.php#php_oop, subsection 'Using ObjectRef in automatic fields'.

It says that the syntax shown as
[onshow.~item2.methY(a,b)]
should work for [onload] and [var] as well.

But your advice above makes me think I have misunderstood this somehow?

Is the quote-delimiting the problem? e.g., could I instead say
[onload.~MyAssets.add(/css/com1.css,/js/com1/js)]
... where the arguments aren't quote-delimited, which would work as long as asset URLs don't contain commas?

In any case, your alternative suggestion looks like it does the same thing. Thanks again!
By: Skrol29
Date: 2011-11-23
Time: 22:04

Re: Managing CSS & JS files

Hi,

I'm glad you are visiting ans analyzing the code :-)

> Ah, I misread the tbs_class.php source then. I had thought that the argument \' passed to
> f_Loc_PrmRead by meth_Locator_FindTbs on line 851 was used to allow ' as a delimiter.

This line of code indicates that (') is a string delimiter for parameters inside the TBS tag. But in your case, It's not in the parameter but in the name of the tag (name is "onload.~MyAssets.add('/css/com1.css')").
In this case, TBS uses the function f_Misc_CheckArgLst (line 3211 in TBS 3.7.0 for PHP 5). As told in the manual, this function lazily read arguments, without string delimiter.

> Is the quote-delimiting the problem? e.g., could I instead say
> [onload.~MyAssets.add(/css/com1.css,/js/com1/js)]

It is possible that luckily, this tag can work indeed.

> In any case, your alternative suggestion looks like it does the same thing

Yes, absolutely, but it is more all-terrain.
By: Skrol29
Date: 2011-11-24
Time: 00:47

Re: Managing CSS & JS files

hi Doubi,

I'm implementing in TBS 3.8.0 a new feature for this purpose:
(version 3.8.0 is in beta version since several months, and very stable yet)
Tell me if you're interested to beta test.

Here is the plan :
- Parameter "getpart" becomes an alias of "getbody" (because this will be the name in TBS 4)
- New parameters "store" and  "storename".

Example :

Main template:
<html>
  <!-- topLevelTemplate.html -->
  <head>
    [onshow..store.getpart]
  </head>
  <body>
    [onload;file=component1.html;getpart=body;store=(link)+(script)+(style)]
    [onload;file=component2.html;getpart=div;store=include]
  </body>
</html>

Component 1:
<html>
  <head>
    <script type="text/javascript" src="scripts.js"></script>
    <link href="style.css" rel="stylesheet" type="text/css">
    <style type="text/css"> ... </style>
  </head>
  <body>
    ...
  </body>
</html>

Component 2:
<include>
  <script type="text/javascript" src="scripts.js"></script>
  <link href="style.css" rel="stylesheet" type="text/css">
  <style type="text/css"> ... </style>
</include>
<div>
  ...
</div>

Parameter "storename" is optional. Its default value is "getpart" in this context. It enables you to define a different name for the store.
By: doubi
Date: 2011-11-24
Time: 18:54

Re: Managing CSS & JS files

Hi Skrol29,

That looks really cool! Yes I would be interested in beta testing 3.8.0, and staying up to date.

Do you have a git repository or something which I could pull updates from?

To give you some context for my use-case, I'm trying to bring some sanity to a large legacy codebase which hasn't used any real templating system at all before, so TBS really suits because it's so light-weight and I can add it to existing code very easily.

My only request in addition to what you showed above is that getpart and store could understand basic CSS selectors, e.g.
[onload;file=component1.html;getpart=div#first]
[onload;file=component2.html;getpart=div.myclass]
The above would grab the div with the xml id attribute of 'first' from component1.html, and all divs with CSS class 'myclass' from component2.html.

I think this could be really useful, but I realise it would probably be a lot more parsing work!

In any case, in the existing code, what happens if getpart finds multiple elements in the subtemplate which match?

So in your example, what happens if component2.html contains multiple <div>s? Is only the first <div> taken? Or would they all be inserted in the order they're found in the subtemplate?

And what would happen if component2.html looked like the following?
<div>
  Some content here
  <div>
    Oh no! Another &lt;div&gt;!?
  </div>
</div>
By: Skrol29
Date: 2011-11-25
Time: 00:32

Re: Managing CSS & JS files

Hi,

> Do you have a git repository or something which I could pull updates from?

The SVN repository is at SourceForge.
The like is available at the page: http://www.tinybutstrong.com/support.php

> [onload;file=component1.html;getpart=div#first]
> [onload;file=component2.html;getpart=div.myclass]

To define a block with the "id" attribute is something that is studied for TBS 4, but not easy to code.
To define a block with the "class" attribute is much more complicated, and too specific to HTML.

> In any case, in the existing code, what happens if getpart finds multiple elements in the subtemplate which match?

Elements are concatenated in the order they are found.
But in case of encapsulated elements of the same type (your example with <div>), the result is not yet framed. It can produce bad results with the current version.






By: doubi
Date: 2011-11-25
Time: 01:50

Re: Managing CSS & JS files

Hi Skrol29,

Thanks for the pointer to the repo, should have seen that. (I really recommend git by the way :-)

> Elements are concatenated in the order they are found.

Ok, that makes sense.

> But in case of encapsulated elements of the same type (your example with <div>), the result is not yet framed. It can produce bad results with the current version.

To be honest I don't know what I would expect the 'correct' behaviour to be in that situation anyway :-) Seems like a fundamentally ambiguous request, unless you could make it more specific with attribute selectors.

> To define a block with the "id" attribute is something that is studied for TBS 4, but not easy to code.

Well, I think my templates will be in the simpler form you gave in examples above anyway. If I discover I really need attribute selectors maybe I can help scratch that itch myself :-)

> To define a block with the "class" attribute is much more complicated, and too specific to HTML.

I assume this is because the 'value' of the attribute can actually be a list of values (class="style1 style2") which need to be parsed out? It hadn't occurred to me that this was an irregular bending of standard XML rules.

Thinking about it, I suppose if one went to the trouble of implementing what I describe above, one would have a fully generic XML attribute locator & parser, and #id and .class would just be special 'shortcuts' for common CSS selectors. You're right, it does seem like an over-investment of effort, unless a lot more people really wanted it.

Anyway, thanks for all your help and support. I'll start playing with the 3.8 code.
By: Skrol29
Date: 2011-11-25
Time: 15:55

Re: Managing CSS & JS files

Hi,

> I assume this is because the 'value' of the attribute can actually be a list of values (class="style1 style2") which need to be parsed out?

Yes, indeed.

> It hadn't occurred to me that this was an irregular bending of standard XML rules.

It is a standard of XML. When attributes may have several values, separate them by space is a standard.
Nevertheless, TBS does not process a deep XML parsing.
Admin mode