Page 1 of 1

Best practice vs javascript in module templates

Posted: Fri May 09, 2014 1:18 pm
by psy
It's generally accepted best practice to load javascripts after the content of your page. Which is fine until you have a module that by default inserts javascript in its template. If you haven't pre-loaded any dependencies, eg jQuery in the head section, you're up the creek.

My solution is to load all dependencies esp jQuery as per 'best practice' after the content. Then in the module template, capture the script into an array and add it after the dependency. Eg:

In my module template:

Code: Select all

{capture assign=js}
{literal}
<__script__>
$(document).ready(function()({
...
});
</__script>
{/literal}
{/capture}
{append var=pagejs value=$js}
Then in my page template immediately above the <__body> tag:

Code: Select all

<__script__ src="source/to/jquery"></__script>
{if is_array($pagejs)}
{foreach from=$pagejs item=js}
{$js}
{/foreach}
{/if}
This drops all module template js (or hidden popup divs etc) at the bottom of the page.

All worked well until this week I tried it with CGGoogleMaps2. Sure, the script went to the bottom of the page but so did the displayed map!

Solution was to on my page:

Code: Select all

{capture assign=js}{CGGoogleMaps2 map='mymap'}{/capture}{append var=pagejs value=$js}
and in the map template:

Code: Select all

{* map template *}
<__script__ src="http://maps.google.com/maps/api/js?libraries=geometry{if $map->sensor}&sensor=true{else}&sensor=false{/if}"></__script>
<__script__ src="{$mod->GetModuleURLPath()}/lib/js/infobox_packed.js"></__script>
<__script__ src="{$mod->GetModuleURLPath()}/lib/js/map_tooltip.js"></__script>
<__script__ src="{$mod->GetModuleURLPath()}/lib/js/jquery.cggm_map.js"></__script>

<div id="cggm_map_{$mapinstance}" class="col-sm-12 cgmap"></div>
{if $map->directions}
  {* enable directions *}
  {$generator->get_directions_form()}
{/if}

{* javascript is on the bottom incase some smarty variables we need were set in the various generator calls *}
<__script__ type="text/javascript">
if( typeof(jQuery) == 'undefined' ) {
  var div = document.getElementById('cggm_map_{$mapinstance}').
  div.innerHTML = '<h3 style="color: red;">jQuery and jQuery UI Are Required</h3>';
  throw new Error('jQuery and jQuery UI Are Required');
}
$(document).ready(function(){
  var obj = $('#cggm_map_{$mapinstance}');
  obj.cggm2({$generator->get_map_options_js()});
  {if $map->directions && isset($directions_form_id)}$obj.cggm2('options','directions_form','#{$directions_form_id}');{/if}

obj.detach().appendTo('.maintext');

});
</__script>
Note the "obj.detach().appendTo('.maintext'); line.

The important things above were that the module was executed in the page so all the vars were filled, then in the script, the displayed map gets moved from below the page to the right place within the content with '.maintext' class .

Everything we do is a compromise and it's been said before that {capture} is not hugely efficient. On the other hand, the output scores way higher on page audits using the above technique.

Just saying...

psy

Re: Best practice vs javascript in module templates

Posted: Fri May 09, 2014 1:26 pm
by Jo Morg
psy wrote:Everything we do is a compromise and it's been said before that {capture} is not hugely efficient.

Code: Select all

{CGGoogleMaps2 map='mymap' assign='js'}{append var=pagejs value=$js}
Wouldn't this work? I seem to recall that by default in CMSMS all module calls could be assigned directly. Didn't test this with CGGoogleMaps2 though.
But nice tip indeed.

Re: Best practice vs javascript in module templates

Posted: Fri May 09, 2014 1:28 pm
by psy
Very probably JoMorg :)
Guess my thinking was stuck.
It's good to share ideas.

Re: Best practice vs javascript in module templates

Posted: Fri May 09, 2014 2:15 pm
by uniqu3
JoMorg is right, smarty tags, module tags all can be assigned with assign.
Regarding capture, why not simply assign, only drawback is that you need to take care of proper escaping or not mixing up single and double quotes.

Code: Select all

{$js = "
<__script__ type=\"text/javascript\"> <!-- double quote needs escaping -->

$(document).ready(function() {
    console.log('Yay ready'); // single quote not
});
</__script>
"}
But ideal solution would be a simple block type smarty plugin, so you would simply do something like {js_block assign='js'} my js content {/js_block} or actually i think CGExtensions comes with {jsmin} {/jsmin} or so, so that might work to, only problem is it minifies your code, which is nice for production, not so nice while developing :-D

Re: Best practice vs javascript in module templates

Posted: Fri May 09, 2014 2:34 pm
by psy
Thanks uniqu3

I actually do encase all my js in {jsmin} but not everyone installs CGExtensions so I removed it from the above example. Hehe, I like your idea.... maybe simply {jsmin assign='js'}?

While the focus seems to be on my (resolved) issue with CGGoogleMaps2, the main point of the tip was to capture module template scripts/hidden divs etc and drop them to the end of the page without breaking the page.

Gladly accepting suggestions to make the solution more elegant.

Re: Best practice vs javascript in module templates

Posted: Tue May 13, 2014 10:32 am
by fprm67
Hi,
I would use {jsmin} {/jsmin} in a template of the Gallery Muldule, but it does not work. You have any suggestions?
Another question that how I could do minify css in a template of the Gallery Module?

Thank you

Re: Best practice vs javascript in module templates

Posted: Tue May 13, 2014 8:56 pm
by psy
The Gallery module does not evaluate smarty tags in the Javascript or CSS textareas of the template.

To move the js to the end of your page, you would have to assign it in the layout textarea where you have your smarty tags.

Not tested, but for the CSS, maybe you could also put it in the layout section encased in {strip assign='gallerycss'} tags then put the {$gallerycss} tag in the head section of your page template? You will of course have to include the start and end <style> tag in the var.

The body area of a page is processed before the head section so the {$gallerycss} var should be available.

Alternatively, you could put the {$gallerycss} in the metadata box on the Options tab of your page edit screen of the page containing the Gallery tag.

Re: Best practice vs javascript in module templates

Posted: Fri May 16, 2014 9:13 am
by Rolf
Kind of related to this topic. I made a blog about combining JS files into one. Very simple solution, almost too good to be true. Can't think of any cons at the moment...
https://www.cmscanbesimple.org/blog/eas ... s-and-code

Re: Best practice vs javascript in module templates

Posted: Fri May 16, 2014 9:40 am
by psy
Sweet idea, Rolf. There are definitely benefits in loading only one js file. :)

BTW, JoMorg, had occasion to use CGGoogleMaps2 on another site today with the extra jQuery obj.detach().appendTo('.maintext'); line in the map template.

Using my method and your tag suggestion, ie {CGGoogleMaps2 map='mymap' assign=js}{append var=pagejs var=$js} worked a treat!

Just pure CMSMS, module templates and the magic of Smarty.

Re: Best practice vs javascript in module templates

Posted: Thu May 29, 2014 10:58 am
by psy
Re uniqu3's comment about every tag having the ability to be assigned...

Absolutely this is true but found an example today that caused a problem.

Older tags, esp plugins, do assign but a few also output the result at the same time.

I wanted to assign the {title} tag to {title assign='title'}. The tag was assigned to the var $title but it also output the page title which at that point in my template, was not what I wanted.

Solution was {assign var='title' value="{title}"}

Re: Best practice vs javascript in module templates

Posted: Thu May 29, 2014 11:02 am
by uniqu3
I always assign title Tag and it never outputs any things except when explicitly calling assigned variable.

Re: Best practice vs javascript in module templates

Posted: Thu May 29, 2014 11:07 am
by psy
Yes, realised that after I'd posted it. The tag wasn't 'title', was some other old tag. Solution was the same.