Jump to content

How to insert scripts and stylesheets from your module?


hdesigns
 Share

Recommended Posts

I open this topic because in another one (http://processwire.com/talk/topic/1732-shop-for-processwire-apeisa/) there was a discussion going on that -in my point of view- went off-topic.

Here's what it's all about:

Say you want to create a module that needs some additional scripts/stylesheets what is the best way to accomplish that?

Right now I only know three ways (which all have their weaknesses):

First: Just edit the head.inc and include those scripts in the head-tag.

OK, this is pretty easy but it has two drawbacks:

- The scripts are now on every page (in the front-end)

- You have to edit the head.inc*

Second: Include

<?php foreach($config->scripts->unique() as $file) echo "\n\t<script type='text/javascript' src='$file'></script>"; ?>

in your head.inc and add the scripts from your module with

$this->config->scripts->add($this->config->urls->PaymentExample . "script.js");

This seems pretty neat because the scripts will only be included where they are needed, but:

- It is not very flexible. What about in-site scripts?

- You have to edit the head.inc*

Third: Use the hooking- mechanism of PW by adding

$this->addHookAfter('Page::render', $this, 'addScripts');

to the init- function of your module.

This is the addScripts function:

public function addScripts($event) {
		$page = $event->object; 

		// don't add this to the admin pages
		if($page->template == 'admin') return;

               //other mechanisms to ensure the script only loads when this module was called in the front-end
      
      $additionalHeadScripts = '[myScripts...]';

		$event->return = str_replace("</head>", $additionalHeadScripts.'</head>', $event->return); 
	}

Of course this approach also has its drawbacks:

- It is a bit difficult

- It probably makes your PW installation run a bit slower because the addScripts- Methods gets called on every page request.

*Why is it a bad thing to edit the head.inc to provide scripts/stylesheets for your module?

I personally think if you want to distribute a module the admin that wants to integrate this module should just hit the install button and do some configurations and that's it. This is called usability and I believe in it.

In my opinion it is not user/admin friendly if you have to pack a readme file to the module that says "but first you have to alter your
template file to make that module work"...

That said, if

<?php foreach($config->scripts->unique() as $file) echo "\n\t<script type='text/javascript' src='$file'></script>"; ?>

was already in the default template head.inc file I would not hesitate one second to use the second approach for my module. Because if an admin creates his/her own template and it is not based on the default template it's his/her fault...

So, what do you think? Which is the best approach for you and why?

Or do you have another approach?

Maybe it would also be a good thing if there was something in the API that we could use for this need. A more standardized approach?

  • Like 3
Link to comment
Share on other sites

If the module needs JS/CSS in the frontend (e.g. in templates), I don't see any solution other than let the dev add those scripts himself or using the Page::render hook, assuming that the module is autoload.

Why?

Because the module creator can't know your template structure. There are lots of different approaches, using a head.inc file is just one of them.

Adding the scripts/styles to the config arrays will only work if those arrays are used to output the scripts in the templates.

This is because Pw does not force you how to generate the markup, it's pure freedom :)

  • Like 3
Link to comment
Share on other sites

I'm totally confused by this discussion. What do modules have to do with head.inc?

Edit:

OK, I have read Wanze's post and it is beginning to make sense. This is not an everyday case for modules but a situation where interaction with template files is required (right?)...I just needed to clarify lest new comers to modules got confused :D

Edited by kongondo
Link to comment
Share on other sites

  • 2 weeks later...

If I understand this correctly, I think I would choose the third method. This should be in the head section of your html, then then the template structure doesn't matter. I would imagine you can save the css in the module folder and pass the full path/to/module/css/style.css to $config->scripts->unique. 

Using this method does raise some questions, I'm pretty new to PW and don't know how it works yet. 

How are dependancies handled?

How are duplicate files handled? 

Link to comment
Share on other sites

  • 1 year later...

I came across this thread while working on my first PW module that needs to load quite a lot of scripts. So I am going to revive this thread because the OP makes an important point here: how to best find the balance between easy module integration and freedom for PW users when it comes to template structure . 

In my opinion, best practices for script inclusion should also be considered here. So while giving users the most possible freedom in how to structure their templates, they should at the same time be made aware of ways how to best implement script loading. Not only with PW, but in general.

I for myself am still learning a lot about web development best practices while developing sites with PW. There is a lot of useful information to be found here in the forums which is absolutely great.

One thing that most of web devs seem to agree upon is that loading scripts at the end of your template just before </body> increases site loading speed and thus user experience. Besides that it also makes sure that the DOM is already available to those scripts.

And here lies the culprit of approach 3 proposed by the OP: all additional scripts get loaded in the <head> which slows down page loading time.

Thus I would implement the addScripts method like this

public function addScripts($event) {
		$page = $event->object; 

		// don't add this to the admin pages
		if($page->template == 'admin') return;

               //other mechanisms to ensure the script only loads when this module was called in the front-end
      
      $additionalScripts = '[myScripts...]';

		$event->return = str_replace("</body>", $additionalHeadScripts.'</body>', $event->return); 
	}

Only drawback  I can see: following this approach, the module developer has to make sure that all inline scripts wait for the scripts they depend upon before they are executed (eg with $(window).load or $(document).ready)

Link to comment
Share on other sites

One thing that most of web devs seem to agree upon is that loading scripts at the end of your template just before </body> increases site loading speed and thus user experience.

There could be multiple valid reasons to use 'inline' javascripts. Going for an assumption based approach (as 'automaticly' placing before </body>) will steal some developers freedom.

In my opinion:  When developers want to use your module it's not bad they are following your instructions to make it work.

Link to comment
Share on other sites

When developers want to use your module it's not bad they are following your instructions to make it work.

That's true.

But if your instruction go something like: "You need to include these 4 scripts in your head tag to make this module work and you need to download them from these 4 different places first", it may drive people off.

Imagine a developer who cares about page loading optimization and he cannot do it right because of your module. Or he has to go and alter the module code so it fits his needs.

Wouldn't it be better if the module followed commonly accepted best practices and people who use the module don't need to care about things like manual script inclusion?

I personally don't feel really comfortable with the proposed approach 3, too. The $this->config->scripts->add() method seems to be the preferable way to go.

But then you need to instruct the users of your module to include the necessary php for rendering the scripts tags before </body>. Which I consider to be a PW best practice anyway when it comes to conditional loading of scripts.

The only point that I really wanted to make is that we should follow best practices and include our scripts at the end of the page and not in the head. If this limits developers freedom, it at least forces something on them that is generally a good thing.

Link to comment
Share on other sites

I personally don't feel really comfortable with the proposed approach 3, too. The $this->config->scripts->add() method seems to be the preferable way to go.

But then you need to instruct the users of your module to include the necessary php for rendering the scripts tags before </body>. Which I consider to be a PW best practice anyway when it comes to conditional loading of scripts.

This method has a major drawback, as you might load unnecessary scripts from the backend (other modules running in the backend will use this array also for storing scripts/styles). A better apporach IMO would be if the module uses its own array to ensure that only the needed files are loaded.

  • Like 1
Link to comment
Share on other sites

I use AIOM+ and then regular php to manage the array before the AIOM+ is called..

im usually setting the scripts array in the init, like

$minifyCSS = true;
$minifyJS = true;

$stylesheets = array(
    'assets/bootstrap/css/bootstrap.min.css',
    'assets/css/font-awesome.min.css',
    'assets/css/style.css',
    'assets/css/custom.css',
);

$jsfiles = array(
    'assets/js/jquery-2.1.3.min.js',
    'assets/js/imagesloaded.pkgd.js',
    'assets/js/custom.js',
);

in templates i sometimes do this (if styles/scripts need to be before last one, e.g. if last style is custom/override)

$jsOffset = -1;
$cssOffset = -1;

//1.) Event Calendar
array_splice($jsfiles, $jsOffset, 0, 'plugins/eventCalendar/js/jquery.event.calendar.js');
array_splice($jsfiles, $jsOffset, 0, 'plugins/eventCalendar/js/languages/jquery.event.calendar.en.js');
array_splice($stylesheets, $cssOffset, 0, 'plugins/eventCalendar/css/jquery.event.calendar.css');

or simple:

$jsfiles[]	= 'plugins/soundManager/js/soundmanager2-nodebug-jsmin.js';
$jsfiles[] 	= 'plugins/soundManager/page-player/script/page-player.js';
$stylesheets[] 	= 'plugins/soundManager/page-player/css/page-player.css';

then outputting:

if($minifyCSS == true) {
    echo "\t" . '<link rel="stylesheet" type="text/css" href="' . AllInOneMinify::CSS($stylesheets) . '" />' . "\n";    
    
} else {
    foreach($stylesheets as $stylesheet) {
        echo "\t<link href='{$templates}{$stylesheet}' rel='stylesheet'>\n";
    }
} // END IF MINIFY

this lets you toggle AIOM and good for development and troubleshooting stuff..

i should also clarify that for brevity, i setup a lot of aliases in the init, like:

$templates = $config->urls->templates;
  • Like 4
Link to comment
Share on other sites

  • 2 months later...

Hmm.

Wordpress uses a script/styles wp_enqueue_scripts hook and a wp_enqueue_script/style function that is quite intelligent. It allows you to enqueue a script and any dependencies. WP then makes sure the dependency is loaded before the script that depends on it. Even if the dependency is not itself queued to be loaded on that page, the dependency argument will force it to be loaded.

https://codex.wordpress.org/Function_Reference/wp_enqueue_script

For instance, if your script depended on jquery but jquery was not loaded by default (or you wanted to ensure it was), doing this...

function my_scripts_loader(){
  wp_enqueue_script('my_script_handle', 'my_script_url', array( 'jquery' ));
}

add_action( 'wp_enqueue_scripts', 'my_scripts_loader' );

...would load the script previously enqueued with the handle jquery.

And then if your script was itself needed for another dependent script, you could:

function next_script_loader(){
  wp_enqueue_script('next_script_handle', 'next_script_url', array( 'my_script_handle' ));
}

add_action( 'wp_enqueue_scripts', 'next_script_loader' );

So the script load order would be:

jquery

my_script_handle

next_script_handle

Scripts/styles enqueued this way are spat out in the wp_head or wp_footer template function. There's an in_footer argument for putting a script in the footer.

Probably wouldn't be hard to use that system in PW core, or roll your own. It's all about queueing the enqueued scripts and loading any given dependencies in the right order.

  • Like 3
Link to comment
Share on other sites

  • 6 years later...

Reviving this old thread...

I've been deeply thinking about a simplified build process in addition to a more standard, non-NPM/Webpack/etc. way of loading CSS and JS.  I agree with @Nuwanda in that the way WordPress does it is well thought out.  It's pretty straight-forward, but I wonder if ProcessWire could standardize something like wp_enqueue_scripts/styles in the core.  Right now there's no standardization, but that's probably by design (ie, ProcessWire doesn't dictate markup / not a theming CMS).

Link to comment
Share on other sites

I'll release RockFrontend very soon that does exactly that. It will support LATTE for templates (if you want), LESS for stylesheets (if you want) and comes with zero-setup autoloading (both on the frontend and backend).

And you can inject scripts from your modules:

$rf = $this->wire->modules->get('RockFrontend');
$rf->styles('head')->add(__DIR__.'/mystyle.css');

 

  • Like 3
Link to comment
Share on other sites

53 minutes ago, Jonathan Lahijani said:

I agree with @Nuwanda in that the way WordPress does it is well thought out.  It's pretty straight-forward, but I wonder if ProcessWire could standardize something like wp_enqueue_scripts/styles in the core.  Right now there's no standardization, but that's probably by design (ie, ProcessWire doesn't dictate markup / not a theming CMS).

Have to agree with this one — it would be nice if we had a standard approach for this. Having built some plugins for WordPress recently, I must say that it's nice to have a "standard" way to inject scripts/styles. And of course that applies to site development as well ?

$config->scripts and $config->styles are currently the closest equivalent to wp_enqueue_script/wp_enqueue_style we have, and some folks do indeed use them for their front-ends. But this is not particularly "well supported", there's nothing like the "deps" array from WordPress, etc.

Additionally there's one pretty big issue: some modules don't check for e.g. admin context before pushing stuff to said config arrays, and that can easily lead to admin stuff "leaking" into the front-end. This is the biggest reason why I don't rely on these (on the front-end) myself ?

If this was to be implemented in the core (not saying that it should, just thinking out loud), it might require a whole new concept — something like wireEnqueueScript($script, $deps). And, of course, we'd need to accept that it depends on the site whether this would work or not. To be fair, it's really the same for WordPress: unless theme calls wp_head() and wp_footer(), enqueue functions won't do much. So even there it's up to the theme to make sure that they work.

Site-specific things can of course be cooked up already. Or approaches specific to individual output modules/frameworks, like the approach that Bernhard mentioned above.

--

Just for reference, here's what I've done on some sites — probably not a big surprise that I'm using Wireframe here, and also running things through ProCacheBuster:

// site/templates/wireframe.php

echo $wireframe->render([
    ...
	'footer_scripts' => new FilenameArray(),
    ...
]);

And then in layout file:

		// site/templates/layouts/default.php

		<?php if ($footer_scripts->count()): ?>
			<?php foreach ($footer_scripts as $footer_script): ?>
				<script src="<?= $buster->url($config->urls->resources . $footer_script) ?>"></script>
			<?php endforeach; ?>
		<?php endif; ?>

	</body>

</html>

This way I can push scripts to $view->footer_scripts from anywhere within the site (usually from Controller classes or individual views), and those get injected to the site's (global) footer.

  • Like 2
Link to comment
Share on other sites

31 minutes ago, teppo said:

If this was to be implemented in the core (not saying that it should, just thinking out loud), it might require a whole new concept — something like wireEnqueueScript($script, $deps). And, of course, we'd need to accept that it depends on the site whether this would work or not. To be fair, it's really the same for WordPress: unless theme calls wp_head() and wp_footer(), enqueue functions won't do much. So even there it's up to the theme to make sure that they work.

True, that's why some of my new modules will work on the frontend by default if the site is using RockFrontend for the frontend. Otherwise users will have to include scripts and styles the boring old way...

31 minutes ago, teppo said:

Just for reference, here's what I've done on some sites

I've done it similar on many many sites. But it did not feel right. Too much "foreach" and "if/else" in my templates... Also things look easy at first thought (using FilenameArray), but then you notice it's not enough... What if you wanted to add a custom property to your tag like "defer"? That's another if/else in your template. What about a cache busting timestamp? Another few lines and checks you have to do, because ProcessWire does not have a nice way to convert between paths and urls, which you need, because you need the path for filemtime() but the url for the html tag...

What if you wanted to add a script only on several pages or page types? Another if/else (how many do we have until now?)...

What if you wanted to use LESS instead of CSS? Another module, some more lines of code...

What if you wanted to add all stylesheets of a folder, so that you can split your CSS into logical pieces and move components from one project to another easily? You'd need to add all those files to your main markup file manually...

All that is solved by RockFrontend:

<?php namespace ProcessWire;
/** @var RockFrontend $rockfrontend */
$body = $rockfrontend->renderLayout($page);
?><!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <?= $page->seo ?>
  <?php
  echo $rockfrontend->styles('head')
    ->add('theme/uikit.theme.less')
    ->addAll('sections')
    ->addAll('/site/assets/RockMatrix')
    ->addIf("theme/blogitem.css", $page instanceof Blogitem)
    ->addIf("theme/newsitem.css", "template=newsitem")
    ->add('https://use.typekit.net/xxx.css') // adobe fonts
    ->render();
  echo $rockfrontend->scripts('head')
    ->add('theme/js/uikit.min.js')
    ->add('/site/modules/RockFrontend/lib/alpine.min.js', 'defer')
    ->add('/site/modules/RockFrontend/RockFrontend.js', 'defer')
    // when logged in rockfrontend will inject Alfred.js here!
    // don't remove this rendering block even if you don't add custom scripts
    ->render();
  ?>
</head>
<body>
  <?= $body ?>
</body>
</html>

And since I'm rendering the layout in line 3 (above the <head>) I can inject scripts and styles from any other file that is loaded for rendering.

  • Like 2
Link to comment
Share on other sites

12 hours ago, bernhard said:

I've done it similar on many many sites. But it did not feel right. Too much "foreach" and "if/else" in my templates...

You know what they say about opinions ?

Seriously though, I get your point, and I'm a big fan of keeping code (or rather unnecessarily complex logic) out of the view layer myself. That being said, I don't mind a few foreach and if/else blocks here and there, as long as it doesn't get out of hand.

Looking at my example above, that first "if" seems to be pointless; for a simple use case like this, here's all we really need:

<?php foreach ($footer_scripts as $script): ?>
    <script src="<?= $buster->url($config->urls->resources . $script) ?>"></script>
<?php endforeach; ?>

That's three lines. There's also no cache busting required here, since we're using $buster (and there are also non-commercial alternatives, like Pete's Minify module or AIOM) ?

12 hours ago, bernhard said:

Also things look easy at first thought (using FilenameArray), but then you notice it's not enough...

This is absolutely true. FilenameArray is a rather limited/limiting tool, best suited for relatively simple use cases. In the example above I used that because it was enough for that particular use case.

And, to be honest, it's been enough for most use cases for me — but if something more nuanced is needed, then another data type (or another approach altogether) is likely going to be a better choice.

12 hours ago, bernhard said:

What if you wanted to add a script only on several pages or page types? Another if/else (how many do we have until now?)...

What if you wanted to use LESS instead of CSS? Another module, some more lines of code...

What if you wanted to add all stylesheets of a folder, so that you can split your CSS into logical pieces and move components from one project to another easily? You'd need to add all those files to your main markup file manually...

From my point of view the first one here is not a big issue (I don't usually have that many scripts, so this can be handled with a couple of if-else blocks at tops, or a variation of the technique mentioned above, or Wireframe's placeholders) while other two I prefer to handle via build tools. This is not to say that these are not real concerns, mind you — just that they are (mostly) non-issues for me in my workflow ?‍♂️?

Anywho, it'll be interesting to check out RockFrontend once it's available. It sounds like an interesting tool ?

  • Like 2
Link to comment
Share on other sites

Slightly off-topic, but what is the correct way to manage frontend packages without a Node-based method (grunt/bower/webpack)?  Does a formal way to do this even exist?

  • Composer is not meant for frontend packages (right?), but I see that you can get Bootstrap and UIkit from there (but not HTMX or Alpine.js for example)
  • wget into your themes folder?
  • Make a ProcessWire module for each frontend package (that you as a module maintainer manage the versions of yourself)?  This is more WordPress / non-developer like for those that consume the package as opposed to the module maintainer.

I'm really trying to become Node free... not because it's causing me problems, but just because I feel I barely need it except for package management.

Link to comment
Share on other sites

2 hours ago, Jonathan Lahijani said:

Slightly off-topic, but what is the correct way to manage frontend packages without a Node-based method (grunt/bower/webpack)?  Does a formal way to do this even exist?

  • Composer is not meant for frontend packages (right?), but I see that you can get Bootstrap and UIkit from there (but not HTMX or Alpine.js for example)
  • wget into your themes folder?
  • Make a ProcessWire module for each frontend package (that you as a module maintainer manage the versions of yourself)?  This is more WordPress / non-developer like for those that consume the package as opposed to the module maintainer.

I'm really trying to become Node free... not because it's causing me problems, but just because I feel I barely need it except for package management.

You might want to have a look at https://processwire.com/modules/less/ and https://processwire.com/modules/sassify/ for example implementations of parsers. I don't quite see an advantage on compiling frontend assets on the server. Except for themes that want to provide customisations without the need for coding.

One module for each frontend package seems like a reasonable way to go but keeping those up to date will also be quite time consuming for the maintainer. This is where package managers come in handy. So, if you can find composer packages like BS and UIkit that you can utilize for the PW modules than that would be a big time saver.   

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 weeks later...
On 4/24/2015 at 4:37 AM, gebeer said:
public function addScripts($event) {
		$page = $event->object; 

		// don't add this to the admin pages
		if($page->template == 'admin') return;

               //other mechanisms to ensure the script only loads when this module was called in the front-end
      
      $additionalScripts = '[myScripts...]';

		$event->return = str_replace("</body>", $additionalHeadScripts.'</body>', $event->return); 
	}

 

there's of course a typo, it should say $additionalScripts in the last line…

Anyways, hello everybody.

I tried the following (same as above)…

public function init() {
	$this->addHookAfter('Page::render', $this, 'addScripts');
	// ...
}

public function addScripts($event) {
	$page = $event->object;
	$additionalScripts = '<script type="text/javascript" src="/site/modules/MyModule/scripts/mymodulescript.js"></script>';
	$additionalScripts .= '<script type="text/javascript" src="/site/modules/MyModule/scripts/myotherscript.js"></script>';
	$event->return = str_replace("</body>", $additionalScripts.'</body>', $event->return); 
}

Pretty simple, doesn't work though. Even if the path might be wrong (what's the right path to the module's folder anyway?) it should at least include the wrong path where the body tag closes, but nothing happens.

Thoughts?

Link to comment
Share on other sites

22 hours ago, fruid said:

Thoughts?

First of all the better you describe your problem the better our answers can be ? 

22 hours ago, fruid said:

Pretty simple, doesn't work though.

That's not really a helpful description. But as you don't share more specific infos, this is what I'd do:

  1. Inspect the html code of your page and also the network tab and console of your browsers devtools
  2. Install TracyDebugger and add this to your ready.php: bd("ready");
  3. You should see the "ready" dump in your tracy debug bar
  4. Add >> bd("module init"); << inside the init() method of your module and reload the page
  5. You should see the dump in the bar and that confirms that your module loads properly
  6. Then add >> bd("inside hook"); << at the top of your hook to make sure your hook code fires properly.
  • Like 1
Link to comment
Share on other sites

thanks @bernhard, I did all that you suggested. #1 – #5 seem to work fine, I can see 'ready' in Tracy and 3 times 'module init' (why 3 times, no idea).

But no success at #6, the hook seems not to fire, no 'inside hook' or what ever in Tracy, so the hook doesn't fire properly.

I tried putting the hook in the init() and in the ready() function of my module (of course not simultaneously).

So any more suggestions?

Not sure what to look for in the browser dev tools though, I can just say that the markup doesn't get altered at all.

Also, I would actually need to include three scripts in different places of the site, not sure if that's the best practice but some scripts cannot run everywhere as they depend on what's in the DOM and it's just too complicated to bail out in the code when DOM-elements are not present.

Link to comment
Share on other sites

18 hours ago, fruid said:

3 times 'module init' (why 3 times, no idea).

That means that your module is initialized 3 times and it does not have the setting "singular" to true. You can have singular modules that should only be initialized once (like rockfrontend for example) or non-singular modules, that can be initialized several times (like for example all the inputfield modules, where you want to use one module several times with different settings).

// site/ready.php
$wire->addHookAfter("Page::render", function(HookEvent $event) {
  bd("hook fired");
});

I just tried this hook in ready.php and everything works as expected. Just do that on your end and than change things step by step towards your desired solution. Once things break you know where the bug sits and you can fix it. Or as for help with a more detailed error description.

How to you initialise your module? Manually? Or is it an autoload module? Did you read the docs about modules? https://processwire.com/docs/modules/development/

  • Like 1
Link to comment
Share on other sites

I initialise the module manually. Yes I read this doc and many others..

I guess it gets intialised 3 times here because I call upon the module 3 times with wire('modules')->get('asdf'); instead of doing it just once in a php file that loads on each page, which I will change, some methods need to be called everywhere anyway.

The hook fires perfectly fine inside my site/ready.php file and everything works as intended, but not inside the ready() function inside my module it doesn't. 

// ready.php
$wire->addHookAfter('Page::render', $modules->get('asdf'), 'addScripts'); // works

// asdf.module
ready() {
	$wire->addHookAfter('Page::render', $this, 'addScripts'); // doesn't work
}

// asdf.module
public function addScripts($event) {
	$page = $event->object;
	bd("inside hook");
	$additionalScripts = '<!-- :D THIS IS THE SCRIPTS HOOK -->';
	$additionalScripts .= '<script type="text/javascript" src="'.wire('urls')->httpSiteModules.'asdf/scripts/script1.js"></script>';
	$additionalScripts .= '<script type="text/javascript" src="'.wire('urls')->httpSiteModules.'asdf/scripts/script2.js"></script>';
	$additionalScripts .= '<script type="text/javascript" src="'.wire('urls')->httpSiteModules.'asdf/scripts/script3.js"></script>';
	$additionalScripts .= '<!-- THIS IS THE SCRIPTS HOOK :D -->';
	$event->return = str_replace("</body>", $additionalScripts.'</body>', $event->return); 
}

I'm using markup regions BTW.
Also curious about how Tracy Debugger handles scripts, because they are also included at the end of the body tag, don't know when and where that it renders. I'm wondering if that interferes with my hooking, but like I said it works when the hook fires in the actual ready.php file.  

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...