Make sure my .JS file loads every time before others

Neophile picture Neophile · Apr 20, 2015 · Viewed 8.1k times · Source

I have a website where I don't have access to the source but I can manipulate it using Javascript. I have a file called main.js that has been included at the very end of the includes to which I have the access to and I would like to run my custom Javascript code in that file. I have a .JS file with a function called helloWorld() on my server that I would like to load before any $(document).ready() callback fires, because one of the $(document).ready() functions on my website page/pages uses this function.

Custom .JS file:

function helloWorld()
{
alert("Hello World");
}

main.js file on the server (Accessible):

//All the JS code that the website uses
..

// My custom javascript code that includes my custom .JS file
$.getScript("helloWorld.js", function()
{
   // Use anything defined in the loaded script...
});

Now I would like the helloWorld() to be loaded whilst the page is loading and before any $(document).ready() functions fired. I understand that loading this .JS file while the page is loading will possibly slow down the page load. Is there a bullet-proof way of making sure that my custom javascript function will be loaded prior to any $(document).ready()'s? If there is any other way I can achieve this, please do let me know. Looking forward to your suggestions.

Answer

Marco Bonelli picture Marco Bonelli · Apr 22, 2015

Looks like I found a solution for your problem. I wouldn't suggest it, but it's the only way you can load an external script from another one before the DOMContentLoaded event fires.

Solution

Since that your main.js script is in the <head> of your document, you can be sure that it will be loaded and executed before any following part of the DOM. Given this, you can use a synchronous XMLHttpRequest to load your script and execute it.

This kind of technique has some pros and cons:

  • Pros: you can load and execute any script before DOMContentLoaded, and also multiple scripts sequentially.

  • Cons: your document will be frozen until the requests are completed.

Not that bad, if your script isn't enormous it will not drastically impact the loading time. We can still do it.

Implementation

First of all, make sure that your custom.js script is served over a link which will always be reachable, so that your request will not fail. Also make sure that your main.js script hasn't got async or defer properties, so that it will always be executed in the <head>, before the DOMContentLoaded event.

<!-- NOT GOOD -->
<script src="main.js" defer></script>
<script src="main.js" async></script>

<!-- GOOD :) -->
<script src="main.js"></script>

Now that you're ready, in your main.js script you'll need to:

  1. Create and initialize a synchronous XMLHttpRequest object, and send() a GET request to your content.js script.

  2. Create a <script> element, and put the result of your request (which is stored in the .responseText property of the request object) inside it.

  3. Append the script to the <head> to make it run before the DOM is loaded.

Plus, if you also want your script to be removed right after execution (so it will not be visible to the users), you can:

  1. Remove the <script> from the document after it has ran its code. You'll need to listen for the onload event for this.

Also, if you want to make your code run completely anonymously, you can wrap it inside an anonymous function to prevent the access to it from the global scope.

Here's a quick example of what you'll need to do in your main.js file:

(function() {
    // Create the request and the script
    var xhr = new XMLHttpRequest(),
        s = document.createElement('script');

    // Send the request to retrieve custom.js
    xhr.open('GET', 'path/to/custom.js', false);
    xhr.send();

    // Listen for onload, and remove the script after execution
    s.addEventListener("load", function(e) {
        s.parentElement.removeChild(s);
    });

    // Load the code inside the script and run it in the head
    s.textContent = xhr.responseText;
    document.head.appendChild(s);
})();

Now your custom.js script will (anonymously) run before DOMContentLoaded, mission complete!