Make sure my .JS file loads every time before others
Asked Answered
B

2

6

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.

Bandanna answered 20/4, 2015 at 9:5 Comment(2)
Is your main.js file inside the head of the document?Porphyria
Yes it is in the head of the document.Bandanna
P
6

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!

Porphyria answered 22/4, 2015 at 17:1 Comment(12)
shouldn't the x.responseText be: xhr.responseText?Bandanna
@TheNewbie whops, yes you're right, that was a typo! Fixed, let me know if this solution works for you.Porphyria
The solution is pretty good for what I want but I see in the DOM that the whole script component can be seen. Is there a way I can hide/avoid that from being shown?Bandanna
Hello @Marco, I think eval() doesn't work in IE and FF. Any possibly reasons why? Works fine on Chrome. Any solution for IE and FF for eval please? Also it doesn't really work all the time. Not sure why :(Bandanna
@TheNewbie eval() does work on IE. Your error may be somewhere else. What kind of error are you getting?Porphyria
I can see that IE and FF support eval():w3schools.com/jsref/jsref_eval.asp but I'm not sure why it isnt working every time in FF and IE. It fails sometimes and works sometimes.Bandanna
Let us continue this discussion in chat.Bandanna
That's absolutely fantastic @Marco. Thanks for that solution. Bounty well-deserved!Bandanna
Could you suggest a simple change to the xmlhttprequest that would allow me to cache the JS file so that it loads faster the second time onwards?Bandanna
@TheNewbie only the browser controls caching of files, you cannot do much. If your file is reptitively loaded over time and doesn't change its content, the browser may decide to cache it.Porphyria
Hello @Marco, I do get a warning which I forgot to mention earlier. Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check xhr.spec.whatwg.org. Any suggestions on how I can fix this please?Bandanna
@TheNewbie You cannot fix that, you are forced to use synchronous XHRs because you want to load the script synchronously! Just ignore them, they only are suggesting you to avoid this method.Porphyria
S
1

As far as I can see, there are multiple ways of doing this, but the best way would be to use something like Require.js or CommonJS to resolve your dependencies, concat them, and and publish the resulting concatenated javascript file (or many if you can divide your app into multiple sections).

The not-so-great method would be to use the main script to load other scripts by adding script tags, this way you can ensure its there since its the one loading the other scripts.

Sloshy answered 20/4, 2015 at 9:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.