? ?
avatar kavadas
kavadas
12 Sep 2019

What needs to be fixed

In the new backend template the scripts are loaded at the end of the document, right before the body closing tag. While this was a good practice in the old days, it is not anymore. Read below why.

Why this should be fixed

Having the scripts loaded at the end of the page makes both defer and async attributes useless. Using those attributes can improve the performance of the page.

How would you fix it

Move the scripts back to head and use the defer attribute when required. This way the browser will be able to start downloading the scripts earlier without blocking the HTML parsing. And when the document is completely parsed the scripts are executed. In the current implementation, since the scripts are located at the bottom, browser will start downloading and executing the scripts when the document parsing is (almost) completed.

Side Effects expected

Any inline scripts that depend on the scripts loaded in head using the defer attribute will have to listen to the DOMContentLoaded event. But this should not be considered as an issue because it is also a requirement with the current implementation.

Votes

# of Users Experiencing Issue
2/3
Average Importance Score
5.00

avatar kavadas kavadas - open - 12 Sep 2019
avatar joomla-cms-bot joomla-cms-bot - change - 12 Sep 2019
Labels Added: ?
avatar joomla-cms-bot joomla-cms-bot - labeled - 12 Sep 2019
avatar franz-wohlkoenig franz-wohlkoenig - change - 12 Sep 2019
Labels Added: ?
avatar franz-wohlkoenig franz-wohlkoenig - labeled - 12 Sep 2019
avatar dgrammatiko
dgrammatiko - comment - 12 Sep 2019

There is one small detail here: inline scripts cannot be deferred, so even if script tags with src can be deferred, inline script tags will always execute as soon as they're parsed (blame IE for this). This can lead to race conditions as an inline script could have a dependency on a file that needs to be fetched/parsed/executed but the timing (even if the DOMContentLoaded event is used in the inline script) could be seriously messed up.

All and all:

  • Move away form inlining scripts
  • If you need to pass data from PHP world to JS use the addScriptOption()
  • Separate concerns, eg don't echo onclick="someFunction" but rather handle everything in the js file.
avatar kavadas
kavadas - comment - 12 Sep 2019

@dgrammatiko Can you please clarify? DOMContentLoaded is not triggered until all scripts are downloaded and executed. So inline scripts using DOMContentLoaded should be safe.
Regarding your other recommendations, I couldn't agree more.

avatar dgrammatiko
dgrammatiko - comment - 12 Sep 2019

DOMContentLoaded is not triggered until all scripts are downloaded and executed.

In theory you're right it shouldn't. rAF is also supposed to run at the start of the paint cycle (or whatever name has this js loop) but safari and edge are firing the event at the end of the cycle...
Although Evergreen browsers are way better than the ones in the browser war years there are still hiccups that we as devs need to test and find solutions to work around them. FWIW I haven't tested lately the consistency of the event as I moved away for using it as an initiator on my js (make everything async or totally sync it doesn't really matter as far as all the scripts are at the end of the body tag)

avatar franz-wohlkoenig franz-wohlkoenig - change - 12 Sep 2019
Status New Discussion
avatar kavadas
kavadas - comment - 13 Sep 2019

@dgrammatiko Thanks for clarifying. I am using async/defer in really high traffic web sites and I have no reports for issues. It seems that the issues you are describing have been resolved already.

In any case, shipping a backend template which does not allow the developers to add scripts to the head is not acceptable. The scripts should go back to head and if we want to mimic the "scripts at the bottom" behaviour we can always use defer.

avatar miljan-aleksic
miljan-aleksic - comment - 18 Nov 2019

Hi, we have started adapting our code for J4 compatibility and we have run into the script execution race issue, we have a lot of scripts depending on jQuery which until now was synchronous loaded in the head. While we agree that it is not the best practice we don't see any reasonable decision to making this braking change in J4.

As kavadas points out, rendering scripts in the body is not anylonger considered a good practice and it should be done instead using async/defer. Furthermore, gaining discusable speed loading times at the expenses of consistency and stability on a backend area doesn't seems to help anybody.

avatar dgrammatiko
dgrammatiko - comment - 19 Nov 2019

@miljan-aleksic make sure that you’re using the cms’ API for your scripts!!! If you do then there is no breaking change. If you’re not then you have to respect the cms’ guidelines, in short “use the API”

avatar brianteeman
brianteeman - comment - 19 Nov 2019

@dgrammatiko could you link to the documentation for this please as I'm sure it will really help

avatar kavadas
kavadas - comment - 19 Nov 2019

@dgrammatiko There is breaking change, any inline scripts that depend on others will stop working. However, this is not the point of this suggestion. The point is that the defer and async attributes turn useless and that developers are not able to place scripts in the head.

avatar dgrammatiko
dgrammatiko - comment - 19 Nov 2019

There is breaking change, any inline scripts that depend on others will stop working

No there is not any B/C break as long as you're using the CMS' API If you have <script> tags echoed in your output then yes there is a break! BUT you should respect the API of the system you are programming against...

@brianteeman sure, here you go: https://docs.joomla.org/Adding_JavaScript

avatar miljan-aleksic
miljan-aleksic - comment - 19 Nov 2019

@dgrammatiko @brianteeman, I know about the CMS API and even though Joomla it self is not using it all the times (maybe that changed now) I would agree on the consense of using it.

What I don't understand is the motivation behind the decision of forcing it use now which will definitely break the majority of extensions and most worrying make the developers spend a considerable time to first, understand what is going on, and then apply a major fix on their code.

The braking changes are already large, why introducing this one? There are literally no advantages except forcing everying to use the API.

avatar dgrammatiko
dgrammatiko - comment - 19 Nov 2019

will definitely brake the majority of extensions and most worrying make the developers spend a considerable time to first, understand what is going on, and then apply a major fix on their code

You mean wrap your inline script with a $document->addScriptDeclaration('Script goes here'); is too much work?

Anyways I'm in favour of respecting the hosting platform API...

avatar miljan-aleksic
miljan-aleksic - comment - 19 Nov 2019

@dgrammatiko the scripts most of the times are dynamic with php parts on it <?php echo ... ?>. Remember they are part of the HTML layout, they could even rely on that specific layout execution logic to render the script.

Moving that syntax to a more abstracted and restricted scenario, yes, it is a lot of work.

avatar dgrammatiko
dgrammatiko - comment - 19 Nov 2019

@miljan-aleksic if your using PHP to create your javascript scripts you're doing it wrong. Both javascript and css are static assets!!! If you want to pass data from PHP to your javascript use either the html elements with some data attributes OR use the Joomla's API addScriptOption. You're using very old programming patterns there...

avatar miljan-aleksic
miljan-aleksic - comment - 19 Nov 2019

I agree @dgrammatiko, it's a very old and bad pattern. But if Joomla 4 is going to force updating those, at least it should not do it by introducing new bad patterns (the topic of this issue). The issues with the current approach are significant:

  1. As no script is rendered in the head, no script would possibly have early access to the DOM.
  2. Placing a script at the end of the body is not exactly the same as using defer.
  3. Emulating defer by placing the scripts in the body and then emulating as well a defer for all html scripts by rendering them after, it's a bad pattern on itself that years from now will be regreting.
  4. There is virtually no performance win as jQuery and possibly other scripts will be loaded and cached by the browser and after the first loading, the defer will not be noticable at all.

As pointed out previously, J4 is introducing unnecessary braking changes plus a new bad pattern without any reasoning behind. Or at least any reasoning so far that anybody was able to explain.

avatar kavadas
kavadas - comment - 19 Nov 2019

No there is not any B/C break as long as you're using the CMS' API If you have <script> tags echoed in your output then yes there is a break! BUT you should respect the API of the system you are programming against...

@dgrammatiko

  1. The core installer is still using inline scripts:
    https://github.com/joomla/joomla-cms/blob/staging/administrator/components/com_installer/views/install/tmpl/default.php#L84-L97

  2. In real life scenarios there are cases where you do need inline scripts. Scripts that are required to run right after some markup is available.

  3. We are trying to solve the problem the wrong way. If we want to improve performance we need to use defer/async instead of making them completely unusable.

  4. Not allowing developers to place a script at the document head is not acceptable.

The last two are really important I think.

avatar fevangelou
fevangelou - comment - 20 Nov 2019

A suggestion...

The default script positioning can be at the bottom of the document.

But since we've been ordering <head> includes in Joomla for over a decade, we could still do this for J4, albeit a bit different to allow for script positioning.

E.g. add an API parameter/flag to allow loading JS scripts inside the <head>. A developer could choose which libs to move there (e.g. jQuery, if it's bundled with J4).

So this JHtml::_('jquery.framework'); could be changed to JHtml::_('jquery.framework', 'head'); for example and enforce the core JS lib to move to the <head>.

Similarly, doing something like $document->addScript('some.js', 'head'); would allow devs to position their scripts at the <head>.

The 'head' flag would obviously need to be ordered after any other parameter that can now go in the JHtml::_(), addScript() and addScriptDeclaration() methods.

As a sidenote, although loading scripts at the bottom would make a "traditional" page appear faster to the user, there may also be a flash of unstyled content, especially if the backend component UI and/or data are augmented with JS.

And in any case, allowing script positioning (as suggested above) is not a mega change to the J core that takes days to implement.

P.S. The Joomla core team should be friendly to developers and not alienate them to other platforms like cough WordPress cough which carries backwards compatibility as their flag. That is, of course, if you still care about Joomla not being a hobby toy for a select few people...

avatar alikon
alikon - comment - 20 Nov 2019

And in any case, allowing script positioning (as suggested above) is not a mega change to the J core that takes days to implement.

so why don't you propose a PR ?

avatar fevangelou
fevangelou - comment - 20 Nov 2019

Cause I value my time - in case there is unanimous negative response, that's a waste of time. Imagine if we did a pull request for every idea suggested...

avatar alikon
alikon - comment - 20 Nov 2019

filling an issue when having a proposal and don't send a PR is a waste of volunteer's times which probably are valuable like your

avatar fevangelou
fevangelou - comment - 20 Nov 2019

a) I didn't file the issue.
b) Your comment does not add anything productive to the discussion.

Please read the entire thread before commenting just for the sake of commenting...

avatar dgrammatiko
dgrammatiko - comment - 20 Nov 2019

FWIW as all my responses here sounded like I'm defending someone else's decision to move the scripts to the bottom of the page (without even an agreement from the PT, iirc), I need to clear things up a bit here:

  • inline echoed scripts will break the CSP in J4, so there should be seriously abandoned
  • the idea to have the ability to render script tags either in the head or at the end of the body was way too old but no-one came up with some B/C code for it
  • the work done from the JS Group for J4 is not complete! The ES6+ files need to be modules and then use a tag <script type="module" for them and a tag <script nomodule for the legacy ES5 files (that enables both world to coexist happily).
  • Using the type=module/nomodule trick (kinda industry standard these days) introduces some challenges, eg modules are by default deferred thus in order to don't break the order of the scripts execution everything needs to move at the bottom.
avatar esedic
esedic - comment - 20 Nov 2019

What @fevangelou (additional parameter/flag for a location, where you want put your script) proposed would be really nice to have. Otherwise in cases, where you need to put script in the head, users will not use CMS API and this is what we don't want to.

Another very useful parameter/flag would be "weight", which would represent the number defining the order in which the JavaScript is added to the page (honestly I don't know, how scripts ordering is covered in current API).

avatar mbabker
mbabker - comment - 20 Nov 2019

Another very useful parameter/flag would be "weight", which would represent the number defining the order in which the JavaScript is added to the page (honestly I don't know, how scripts ordering is covered in current API).

Manual weighting should not be an API feature. If the web asset API would have been designed right instead of turning into the hot mess it is now, that API would have allowed proper declaration of dependencies between assets. Whereas what you have with the various scripts and stylesheets stuff in the Document today and over the last 10+ years is "first in first out" logic. But, seems getting the asset management API into a state that makes Joomla more than a "hobby toy for a select few people" isn't high on the priority list either.

avatar fevangelou
fevangelou - comment - 20 Nov 2019

And anyone who's seen the WP API will know that execution order (for hooks - similar to Joomla's plugin events) is misused all the time.

I consider the current state of "framework goes first, then everything else" is just fine. And for plugins that usually inject CSS/JS in pages where other extensions may also render, we can simply re-order them in the plugin manager and resolve any CSS/JS conflicts (unlike WP).

avatar schultz-it-solutions
schultz-it-solutions - comment - 22 Jan 2020

I also just stumbled across this issue (and yes, our extension in question does use "inline js-scripting" for certain functionality). Having to redesign all of these instances where we use this, is much more work than just
$document->addScriptDeclaration('Script goes here');

I agree with the overall concept, but I would actually prefer to have an intermediate solution like

$document->addScript('some.js', 'head');

so we can step by step reduce this "old behaviour", without having to "rush-in" in order to be ready for J4 when it launches...

avatar brianteeman
brianteeman - comment - 21 May 2021

unless I am very much mistaken scripts in the admin are not currently

loaded at the end of the document, right before the body closing tag.

and this issue can be closed.

avatar kavadas kavadas - change - 22 May 2021
Status Discussion Closed
Closed_Date 0000-00-00 00:00:00 2021-05-22 13:04:17
Closed_By kavadas
avatar kavadas kavadas - close - 22 May 2021
avatar kavadas
kavadas - comment - 22 May 2021

@brianteeman Not sure when this changed, but just checked and scripts are indeed loaded in the head right now:
https://github.com/joomla/joomla-cms/blob/4.0-dev/administrator/templates/atum/index.php#L94

So I am closing this. Thank you everyone.

Add a Comment

Login with GitHub to post a comment