Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update how we load JavaScript in browser #2639

Closed
10 tasks done
Tracked by #2621
vanitabarrett opened this issue May 23, 2022 · 2 comments · Fixed by #3726
Closed
10 tasks done
Tracked by #2621

Update how we load JavaScript in browser #2639

vanitabarrett opened this issue May 23, 2022 · 2 comments · Fixed by #3726
Assignees
Milestone

Comments

@vanitabarrett
Copy link
Contributor

vanitabarrett commented May 23, 2022

What

We're updating how browsers should load our JavaScript to ensure it only runs in browsers that are actually capable of executing it, letting older browsers show our components in their non-JavaScript state.

This involves:

  • govuk-frontend providing code that can be parsed in any browser supporting <script type="module">
  • recommending to use <script type="module"> to avoid scripts being downloaded in older browsers

Why

We have decided to reduce support for IE11 in GOV.UK Frontend and drop support completely for IE8-10. We shared these proposed changes with the frontend community in GDS and across government, and have only received positive feedback.

As part of this proposal, we said we would stop shipping JavaScript to IE browsers. These browsers will fall back to a no-JavaScript experience.

Who needs to work on this

Developers; Tech Writer

Who needs to review this

Developers

Done when

Update JavaScript loading

  1. 1 of 4
    romaricpascal
  2. 5 of 5
  3. 1 of 1
    interoperability javascript tooling
  4. 4 of 4
    javascript
  5. javascript
  6. 1 of 1
    sass / css
  7. 3 of 3
    javascript
    claireashworth romaricpascal
  8. Decision Record
    romaricpascal
  9. Decision Record
    colinrotherham
@querkmachine
Copy link
Member

Changing the <script> element to use type="module" is unfortunately not a complete quick fix. Defining it as a module scopes all of the variables within it, so GOVUKFrontend never becomes globally available on window.

That association is currently created through Rollup's bundling process, and I couldn't find anything in the Rollup docs that would create an explicit window.GOVUKFrontend = [all.js exports] line of code. I did get this approach working with a few other changes though.

Changing the Rollup configuration in tasks/gulp/compile-assets.js to output ESM files…

      return gulp.src(file)
        .pipe(rollup({
          // Used to set the `window` global and UMD/AMD export name
          // Component JavaScript is given a unique name to aid individual imports, e.g GOVUKFrontend.Accordion
          name: moduleName,
-         // Legacy mode is required for IE8 support
-         legacy: true,
-         // UMD allows the published bundle to work in CommonJS and in the browser.
-         format: 'umd'
+         format: 'es'
        }))

…and changing the initialisation code in HTML:

- <script src="/public/all.js"></script>
- <script>window.GOVUKFrontend.initAll()</script>
+ <script type="module">
+   import { initAll } from '/public/all.js'
+   initAll()
+ </script>

This seems to work as desired. JS is loaded in modern browsers that understand type="module", and isn't loaded in IE and older browsers.

However this still presented some weirdness in IE: styles used when JS is available were still being applied to components like the Accordion, which in that case was making the accordion's content inaccessible.

This is because the code that adds the js-enabled class lives in the template HTML, not in Frontend's JS.

Adding type="module" to the script block in src/govuk/template.njk seemed to fix this.

-    <script{% if cspNonce %} nonce="{{ cspNonce }}"{% endif %}>document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
+    <script type="module" {%- if cspNonce %} nonce="{{ cspNonce }}"{% endif %}>document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>

This probably wouldn't be necessary if this code was present in the compiled JS; or if we made it so that components had individual 'enabled' states. (I imagine this JS is currently separated so that the class is applied as soon as possible and the browser doesn't first render a no-JS version of the CSS, before having to switch to yes-JS styles.) But that might be a discussion for another time.

@colinrotherham
Copy link
Contributor

Was going to add #2639 but that's also done

Can mark this as closed too 🎉

@colinrotherham colinrotherham removed their assignment Oct 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging a pull request may close this issue.

6 participants