4

The situation

I have been attempting to create a react application. The index page that is loaded first contains a basic CSS and HTML loading animation and then the external scripts and CSS in a blocking order right before </body>, with the last script removing the loader animation and showing the page.

The intended purpose of this is to load a very small page (2KB in total) containing a loading animation and then load the 8MB of scripts, stylesheets and images, creating a more seamless user experience.

<html>
  <head>
    ...
    [loader css]
  </head>
  <body>
    <div id="loader">...</div>

    <script src="..."></script>
    <script src="..."></script>
    <link href="..." type="text/css" rel="stylesheet" />

    <script> [remove #loader] </script>
  </body>
</html>

The problem

This works great in Chrome, as it immediately renders the page (loading animation) and then the external dependencies. But because safari has weird, inconsistent, and ostensibly non-deterministic loading practices:

Safari starts loading the contents of the new web page, but does not start rendering it until it has loaded a sufficient amount of information.

this approach does not work; it'll just show the address bar loading indicator (for the dependencies above the closing body tag) and a blank page instead of rendering the HTML straight away.


If anybody has a solution that:

  • won't change the order in which things are loaded on the page
  • works on as many browsers as possible
  • is safe from cross-site scripting vulnerabilities

that would be enormously appreciated. Thank you!

Sam Holmes
  • 1,455
  • 10
  • 28

1 Answers1

1

The real problem is that browsers cannot be certain your JavaScript is safe to skip over until it's been loaded, parsed, and executed. If you were to add the defer attribute to your scripts the page will load and display without waiting for the assets to download - but the removal of the loader will also execute without waiting.

Consider a combination of adding defer to the external scripts and making the loader removal script itself an external defer script. According to some defer scripts are supposed to execute in the order they were specified. (Also take note of the references to async and DOMContentLoaded on that comment.)

React.js might also provide come callbacks that could be used more reliably than DOMContentReady.

Community
  • 1
  • 1
Owen
  • 1,507
  • 11
  • 14
  • Thank you for your reply! As far as I can tell, I don't need the browser to be certain the JavaScript is safe to skip over. I just need to load the scripts/stylesheets in such a way that safari displays the rendered DOM before blocking and showing a white screen while it downloads the scripts/stylesheets. – Sam Holmes Aug 01 '16 at 16:12
  • I think deferring the load will make Safari render the page instead of blocking - it's about Safari being sure it's safe to skip over, not you as the developer. Is it easy to test? – Owen Aug 02 '16 at 01:12
  • I'm sorry for taking so long to accept this, I haven't worked on this project in a while. I'm using this with a bundled `vuejs` application. Displaying the loader in the `
    ... loader ...
    ` and then re-rendering vue over the top once loaded works great using the `async` attribute. I only load one file now so execution order is no longer a problem. Still a weird issue, but this seems to be the only viable workaround for the time being.
    – Sam Holmes Feb 21 '17 at 18:07