Moving

With the introduction of the Symfony UX Initiative a lot of cool things are happening in the Symfony world related to JavaScript. Stimulus already has great integration thanks to the Encore.enableStimulusBridge() feature and Turbo has huge potential to give an SPA-like experience with server-rendered HTML. Related to this, I want to talk about a simple change that just happened to the symfony/twig-bundle recipe. If you start a new project today, the location of the {% block javascripts %} in base.html.twig has moved from the bottom of the page up into <head>: 1 2 3 4 5 6 7 8 9 10 11 12<!-- templates/base.html.twig --> <html> <head> {% block stylesheets %}{% endblock %} +

  • {% block javascripts %}{% endblock %} </head> <body> {% block body %}{% endblock %}
  • {% block javascripts %}{% endblock %} </body> </html>

Historically, this lived at the bottom of the page so that any <script> tags wouldn’t be executed until the page had already finished loading. This was for two reasons:

When your browser sees a normal <script> tag, it waits (blocks the page) while that JavaScript is downloaded and executed. You often want the full HTML to already be available when your JavaScript executes.

So why did we move the javascripts block into <head>? Isn’t that worse? Actually, it’s better, as long as your script tags have the defer attribute.

<script src=”” defer>¶ When your browser sees a <script tag with a defer attribute, it treats it differently. First, it still starts downloading it immediately… but your browser does not wait for it to be downloaded before rendering the rest of the page: it is “non-blocking”. And second, after the JavaScript is downloaded, your browser waits until the page is fully-loaded before executing the JavaScript. In other words: your browser starts downloading the file earlier (without blocking the page) but your code is still executed after the page is loaded: the same as having it at the bottom of the page. So as long as your add the defer attribute, life is good! Well… except for “inline” JavaScript: it behaves differently.

The “Caveat”: Inline JavaScript Cannot be Deferred¶ When you use <script src="/app.js" defer>, that code will execute after the page finishes loading, just like if the <script> tag were at the bottom of the page. But inline JavaScript cannot be deferred. Imagine you have an app.js file that creates a global variable: 1 2 3 4 5// app.js // set a global "App" variable window.App = { // ... }

And then you write some “inline” JavaScript in your template that uses it: 1 2 3 4 5 6 7 8 9<html> <head> <script src="/app.js" defer></script> <script defer> // try to use the App variable from app.js App.initializeSomething(); </script> </head> <!-- ... -->

If you tried this, you’d get an error!

Uncaught ReferenceError: App is not defined If you have multiple <script src="/..." defer> tags, they do render in order. The problem is that “inline” JavaScript cannot be deferred: it always executes immediately. This means that App.initializeSomething() is called before app.js. If you have a lot of inline JavaScript like this, then you may not be able to use defer. No problem: keep your JavaScript at the bottom of the page without defer. But the ideal solution is to move all of your inline JavaScript into external JavaScript files. This can sometimes be tricky if you want to pass a dynamic value into JavaScript. This can be fixed by setting a global variable and reading that in your external JavaScript: 1 2 3 4 5 6 7 8 9<script src="/app.js" defer></script>

<script>

  • // JavaScript is "inline" so we can use Twig to pass a dynamic value

  • App.initializeSomething('{{ someDynamicValue }}');

  • // read this from app.js

  • window.SOME_DYNAMIC_VALUE = '{{ someDynamicValue }}' </script>

In app.js, you can read window.SOME_DYNAMIC_VALUE. Another popular approach is to add data- attributes to an element and read them in JavaScript.

encore_entry_script_tags() and the “defer” Attribute¶ If you use Webpack Encore, then you probably don’t write your <script> tags by hand: you render them with the handy encore_entry_script_tags() Twig function. So, how can we add the defer attribute? In WebpackEncoreBundle 1.9, you can specify - in webpack_encore.yaml - an array of attributes that you want to include on all script tags. To always add defer: 1 2 3 4 5 6# config/packages/webpack_encore.yaml webpack_encore:

...

  • script_attributes:
  • defer: true

That’s it! No change needed to your templates. If you install WebpackEncoreBundle today, you’ll get this config automatically thanks its recipe. Have any questions, let us know! We’ll soon cover all of this on SymfonyCasts. Have fun!

                Sponsor the Symfony project.

http://feedproxy.google.com/~r/symfony/blog/~3/TKCr0S6v7Mk/moving-script-inside-head-and-the-defer-attribute

Created 3y | Jan 19, 2021, 2:20:26 PM


Login to add comment

Other posts in this group

SymfonyOnline June 2024: LIVE (Component) Experience

SymfonyOnline June 2024 is in less than 2 months: on June 6-7, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportuni

Apr 26, 2024, 3:10:09 PM | Symfony
SymfonyOnline June 2024: The big upgrade. All the way up to Symfony 7 and PHP 8.3

SymfonyOnline June 2024 is in less than 2 months: on June 6-7, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportuni

Apr 25, 2024, 1:50:38 PM | Symfony
SymfonyOnline June 2024: Crafting Elegant Symfony Tests

SymfonyOnline June 2024 is in less than 2 months: on June 6-7, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportuni

Apr 23, 2024, 9:40:13 AM | Symfony
SymfonyLive Berlin 2024: Using container's features to manage complexity

SymfonyLive Berlin 2024 start in 2 months: on June 20-21, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportunity!

Apr 22, 2024, 1:10:20 PM | Symfony
A Week of Symfony #903 (15-21 April 2024)

This week, Symfony continued adding compatibility with the upcoming PHP 8.4 version and also focused on tweaking and polishing the new features of the upcoming Symfony 7.1 version. Meanwhile, we publi

Apr 21, 2024, 7:40:03 AM | Symfony
SymfonyOnline June 2024: How to Test an External API with 0 Mocks?

SymfonyOnline June 2024 is in less than 2 months: on June 6-7, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportuni

Apr 19, 2024, 2:40:02 PM | Symfony
SymfonyLive Berlin 2024 This is a test: One-click Cypress.IO E2E testing in 45 seconds

SymfonyLive Berlin 2024 start in 2 months: on June 20-21, get ready for the impressive lineup of speakers and topics. Visit here to learn more and don't miss out on this exciting opportunity!

Apr 19, 2024, 10:10:10 AM | Symfony