0

I'm trying to include a three.js module type script tag dynamically after the page has loaded in my html (or php) page. This I need because google pagespeed insight fails badly on a page where there is three js, giving errors and terrible speed score for the page.

  • I have tried php readfile - works, but not really changing the behaviour of the speed test, I get questionmark result for LCP

  • I was thinking about trying to insert the script with xhr after page load, would that be a solution maybe?

settimeout is not an option here

Any ideas?

Mugen87
  • 28,829
  • 4
  • 27
  • 50
bambadamm
  • 79
  • 9
  • right, do not use settimeout, but do xhr on domcontentloaded or window on load event and you pull that script then – Kresimir Pendic Oct 25 '20 at 14:42
  • Check this out: https://stackoverflow.com/a/40808588/1289713 – Vimal Patel Oct 25 '20 at 14:48
  • How are you loading three.js now? Can you provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of your code? – Dshiz Oct 25 '20 at 14:48
  • @Dshiz heres the page: with normal script tag: https://adambernath.com/lotto/lotto.html with php readfile: https://adambernath.com/lotto/lotto5.php – bambadamm Oct 25 '20 at 14:59
  • @KresimirPendic Can you give me an example of how to do that? I've tried and so far I cant make it work, but I probably am doing something wrong. I've made this so far: ` ` – bambadamm Oct 25 '20 at 15:01
  • @bambadamm Okay, you probably want to bundle the scripts you need using something like [webpack](https://webpack.js.org). This would speed up your site and you wouldn't need a hacky workaround like you are trying, which probably won't help anyway. – Dshiz Oct 25 '20 at 15:07
  • @Dshiz I dont really have a problem with the actual page load speed, if you check the link: adambernath.com/lotto/lotto.html it loads pretty fast considering its 3d. My problem here is failing on google pagespeed test exactly. Since nobody really knows how google factors it into their ranking, its best practice to score well on the pagespeed test for seo purposes. So I don't want to optimize the site, it performs great, I want to get around the pagespeed test by delaying the loading and execution of the three js script – bambadamm Oct 25 '20 at 15:11
  • @bambadamm I inspected your site's performance and the reason it is failing the Google Page Speed test is precisely because of how you are handling your scripts. The script loading process is taking 892ms which is almost a full second. This is why it's failing, and why my suggestion will solve your issue. – Dshiz Oct 25 '20 at 15:13
  • @bambadamm you could also load a compressed/minified version of three.js using a CDN script tag: ``. Or download it directly to your server and use the minified script that way. – Dshiz Oct 25 '20 at 15:25
  • @Dshiz google speed test has a problem with three.js in general, which ive already experienced before, but on pages where pagespeed test wasn't really a concern for me. This site is a different kind of animal. as you can see when simply visiting the link, it loads fast, I am not worried about that part at all. three.min.js wont make any difference in this case. my only concern is to load threejs after the page has loaded so it wont be considered as a pagespeed factor (which is a practice in many cases, check out https://www.cgtrader.com for example, the 3d loads after the whole page) – bambadamm Oct 25 '20 at 15:43
  • cgtrader.com uses webpack – Dshiz Oct 25 '20 at 15:49
  • @Dshiz its a workflow that I don't like, overcomplicates things, and is an overkill for simple static websites. I use only whats necessary to make a site work, html, css and vanilla js, thats it. This workflow is a good way to create websites which are insanely fast and score 100/100 on google pagespeed insight... usually. if there is no three.js. Can you please give me an example of how to load the script tag with xhr so I can try at least if it solves my exact problem? I dont want to implement a workflow which complicates things more rather than simplifying things. – bambadamm Oct 25 '20 at 15:59
  • @Dshiz maybe cgtrader wasn't a good example, as it terribly fails on google speed test, but you get the idea, the page loads and the 3d loads after the page is fully visible - and this is what I'd like to solve to separate the 3d loading off of the page loading. – bambadamm Oct 25 '20 at 16:41

1 Answers1

0

A proper way to manage loading your three.js script could be to use requestIdleCallback().

Once the page is loaded and the browser window is idle, the script would begin loading.

Example:

function loadThreeJS(deadline){
    //your three.js script
}

requestIdleCallback(loadThreeJS)

You can pass in an optional timeout parameter, where if the callback has not completed within the timeout period, it will requeue the callback for when the next event loop is idle.

requestIdleCallback(loadThreeJS, { timeout: 3000 })

Documentation can be found here.

Another way would be to use a Promise and load the script after a DOMContentLoaded event.

The following also uses requestIdleCallback(), but is more specific to your question because it inserts script tags into the document. It was borrowed from here and adapted for use directly in the browser:

If you need the script element to have type=module, set the options variable like this: let options = {timeout: 3000, type: "module"}

let src = "https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js" // your three.js script file source
let options = {timeout: 3000}
document.addEventListener("DOMContentLoaded", function(event) {
    new Promise((resolve, reject) => {
        let { timeout } = options
        if (typeof timeout === 'number') {
        timeout = Math.max(timeout, 1)
    
        if (typeof requestIdleCallback === 'function') {
            requestIdleCallback(loadScript, {
            timeout
            })
            return
        }
    
        setTimeout(loadScript, timeout)
        return
        }
    
        loadScript()
    
        function loadScript () {
            const script = document.createElement('script')
            script.addEventListener('load', () => resolve(script))
            script.addEventListener('error', () => reject(new Error(`Failed to load script: "${src}"`)))
            if (options.type) script.type = options.type
            if (options.charset) script.charset = options.charset
            if (options.id) script.id = options.id
            if (typeof options.noModule === 'boolean') script.noModule = options.noModule
            script.async = options.async !== false
            script.defer = options.defer !== false
            script.src = src
            document.head.appendChild(script)
        }
    })
})

Script load time on my test was 15ms for the ThreeJS script CDN URL.

Dshiz
  • 3,099
  • 3
  • 26
  • 53
  • I've tried (here: https://adambernath.com/lotto/lotto10.html ) it loads fast without any problem, but it still fails on google pagespeed insight (now with NO_FCP error, just like when its normally included in the script tag) thanks for the idea though, it was worth a shot. any other approach that might solve this? ive also tried xhr, that gives a result on pagespeed insight, but without LCP. any other idea? – bambadamm Oct 25 '20 at 17:31