Load Multiple js file after page load - javascript

I have multiple JavaScript files which are loaded before page load which are affecting page speed. I want that my JS files to load after page load though to improve page speed.
I tried to use async and defer but due to dependency to each other, this way is not working out for me. I also tried lazy loading through AJAX but that is also not working for me (using this link https://w3bits.com/async-javascript)
#section Scripts {
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=API_KEY&sensor=false&libraries=places"></script>
<script src="/lib/bootstrap-datetimepicker/js/moment.js" type="text/javascript"></script>
<script src="/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js" type="text/javascript"></script>
<script src="/js/viewcomponent/windowsignup.js"></script>
<script type='text/javascript' src='/js/viewcomponent/kundliregistrationwindow.js' async='async'></script>
}
I use multiple JS files on a page and I want them all to load after page load. Is there any way to do that?

You can use getScript inside document.ready.
$(document).ready(function() {
$.getScript("domainpath/lib/bootstrap-datetimepicker/js/moment.js");
});
It's fetch your js after dom ready.

You can use Jquery's $.getScript() function but then you have to keep an eye on proper loading order. Would recommend more to bundle all necessary files on the server side and only serve a single file

When the browser receives a request for an HTML file the DOM starts to draw. If you place your scripts in the <head> tag the DOM stops from rendering and the browser starts to execute the <script> files.
One solution could be to place your <script> files at the end of the <body>.
Another solution could be to dynamically load your scripts into your page after the page is loaded like this:
window.addEventListener('load', () => {
let script = document.createElement('script');
script.src = './my_lazy_loaded_script.js';
document.body.appendChild(script);
});
If you have multiple files that are dependent on each other you could do something like this using the async/await mechanism:
window.addEventListener('load', async () => {
console.log('page loaded')
try{
await loadFileAsync('./my_first_lazy_load.js');
await loadFileAsync('./my_second_lazy_load.js');
} catch(err){
console.log(err);
} finally {
console.log('Everything loaded')
}
})
const loadFileAsync = (url) => {
return new Promise((resolve, reject) => {
if(url){
let script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
resolve(true);
} else {
reject(false);
}
});
}
If you want to load your scripts as soon as the DOM is rendered, you can replace window.addEventListener('load',()=>{}) with document.addEventListener('DOMContentLoaded', () => {})

You usually use a Scripts section to control the location where your scripts are being rendered in the document. The way browsers work, they will interpret the HTML from top to bottom. If they encounter a <script> tag, they will pause the HTML rendering and interpret the JavaScript first. For external JavaScript files this means that they will be included and interpreted first (if they aren’t marked as defer or async).
As such, a common practice is to add JavaScript at the end of the <body> tag. That way, the scripts will not prevent rendering the document first but they will also execute early enough to be able to immediately have an effect. In addition, since they are executed after the majority of the <body> has already been interpreted, they can directly access DOM elements. So you generally don’t need to listen to another DOM event first before you can do something with the DOM.
For example, this would work just fine:
<body>
<div id="foo"></div>
<script>
var foo = document.getElementById('foo');
</script>
</body>
However, if you had the <script> before the <div> (or inside of the <head>), the script would fail to find the foo element simply because it isn’t added to the DOM yet.
For ASP.NET Core this means that you should make sure that your Scripts section is added at the end of your <body> tag inside your layouts file. So it should look like this:
<!-- (The rest of the layout) -->
#RenderSection("Scripts", required: false)
</body>
</html>
You can also add script tags before the RenderSection call to make sure that those scripts are available on all pages but loaded before other scripts. For example, if you are using Bootstrap and you want to use jQuery, it might make sense to include that globally for all pages. But since you have it before the other scripts, it is available for the other scripts.
As far as dependencies between scripts go, make sure that you include the scripts in the order they need to executed. So if you have script B that depends on functions in script A, make sure to include A before B. But then, without using defer or async, just including them should work just fine.

Related

Defer execution of <Script> tag until after jQuery .load completes

I'm trying to 'include' some generated HTML code on my page with jQuery load (injecting it into #loadhead div):
<script>
$("#loadhead").load("head.php");
</script>
<script defer>
... rest of the code
</script>
I want the second part of the script to load only after head.php is done loading. I tried to enforce execution of the second script tag with defer, but it deson't seem to work - still, once in a while, head.php doesn't manage to load before the rest of the code. What can I do to ensure it is always loaded completely? It generates some JavaScript values that are used by the 'defer' script.
Two options for you:
1. Use a function you call from load's callback
Put your second script's code in a function, so it's loaded but not run, and then call that function from load's success callback.
Example:
<script>
$("#loadhead").load("head.php", otherStuff);
function otherStuff() {
// ...
}
</script>
(I didn't see any reason they should be in separate script tags, so they aren't.)
I'd probably put that all in a .js file and link it rather than having script embedded in the HTML inline.
2. Load the script when load is done
Don't include the second script tag at all; load the script later in the load success callback, either using $.getScript or appending a script element.
Example:
<script>
$("#loadhead").load("head.php", function() {
$.getScript("otherstuff.js");
});
</script>
(I didn't see any reason they should be in separate script tags, so they aren't.)
I'd very much go with Option #1, to avoid unnecessary delays.

Javascript works only on reloading page

On https://bm-translations.de/dolmetscher-franzoesisch-deutsch.php you can see the Header doesnt work like it does here: https://bm-translations.de
I guess out of some reason JS isnt working. But the strange thing is on reloading the page (strg + F5) its working. Whats wrong here?
I am loading Jquery first and in footer my own JS.
This is most likely a race condition. You're probably running your javascript before the DOM has had a chance to load. The reason it works on the second reload is that the browser is using the cached version of the scripts.
There are a few ways you can deal with this:
Make sure you script tags don't have an async attribute. This tells them they're allowed to load in any order they'd like so your script may load before jQuery has had a chance to load.
Make sure your scripts are in the right order. The simplest is to move your scripts into the bottom of the body so that the browser loads it last. Make sure your scripts go after any dependent scripts, such as jQuery.
<script src="jquery.js"></script>
<script src="myscript.js"></script>
</body>
The other option is to use a "ready" event. Since you're using JQuery, you already have access to this by simply wrapping your function in a jQuery object with a callback function.
console.log('this will run before the DOM is loaded');
// wait for DOM to load inside jquery callback
$(function() {
console.log('this will run after the DOM has loaded');
});
console.log('this will also run before the DOM is loaded');
The output of the above code would read:
'this will run before the DOM is loaded'
'this will also run before the DOM is loaded'
'this will also run before the DOM is loaded'
because you have added async attribute to jquery script tag.
you can not use the async attribute here if some other script is depending on jquery.
in https://bm-translations.de/dolmetscher-franzoesisch-deutsch.php
<script async src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script async src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>
while in https://bm-translations.de
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script async="" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>
more about async attribute.

Intercepting script load

What I need is to hook/intercept other external JS load.
I can place js anywhere in document
Example:
<script src="hook.js"></script>
<script src="a.js"></script>
<script src="b.js"></script>
Hook.js should intercept a.js and b.js. Problem is, that when hook.js is executed, i cannot see other scripts (document.scripts contains only hook.js) and document ready event is too late (scripts a.js and b.js are executed).
Is there any way to "see" other script tags, before are executed ?
Thanks for any help
Edit
I need to do any "magic" inside hook.js without modyfing (statically) other HTML.
No jQuery
Credit goes here: https://stackoverflow.com/a/59424277/2016831
You can use a MutationObserver to see what elements are being added to the DOM, and when they are being added, simply change the source code, or if its referencing another URL, just redirect it to your own server, with the original URL as a get parameter instead, and return the modified code that way.
Based on the above answer, you could do something like this:
<script>
new MutationObserver((m, o) => {
let potentialScript = document.querySelector("script + script");
console.log(potentialScript.textContent);
if(potentialScript) {
o.disconnect();
potentialScript
.textContent =
potentialScript
.textContent
.replace(
"})()",
`
window.wow = mySecretMethod;
})()
`
);
}
}).observe(
document.body,
{
childList:1
}
);
</script>
<script>
(function() {
let mySecretMethod = () => {
//does a bunch of evil secret stuff
console.log("HA!");
};
})();
wow()
</script>
<script>
console.log(wow())
</script>
Alternatively you can redirect the HTTP requests with a chrome extension, see https://stackoverflow.com/a/61202516/2016831 for more
If I understand what you're trying to do correctly...
If you can control how scripts A and B are loaded, the best approach is to place them on the same domain as the current page (possibly via proxy), load the files via AJAX, and insert your hooks that way. A library like jQuery as m.casey suggested would make the details of the AJAX and executing the script quite simple.
Otherwise, Javascript does not really have the ability to interact with the parsing of the document (which is what is causing scripts a and b to be loaded in your example, and what would be need to be modified to "intercept" script loading), except by using the evil of document.write to modify the HTML stream. Of course, this only works if hook.js is loaded synchronously (as it is in your example code), if it's loaded into HTML and not XHTML, if you can place a second hook afterwards to postprocess the modified HTML stream, and if you are sure the HTML stream won't escape your mechanism.
For example..
<script id="hook1">document.write("<"+"textarea id='capture'>");</script>
<script src="a.js"></script>
<script src="b.js"></script>
<script id="hook2">document.write("<"+"/textarea");</script>
<script id="hook3">doSomethingWith(document.getElementById("capture").value)</script>
Note that this is a huge hack and you probably shouldn't be doing it.
If you're using jQuery, you could have hook.js load the scripts you wish to intercept as follows:
$.getScript("a.js");
$.getScript("b.js");
This would dynamically create the script tags and you would be certain that hook.js would always proceed a.js and b.js.

How to wait until DOM has loaded before adding script tags to <head>?

I'm implementing this Developr theme from themeforest into a Meteor app.
I'm currently accomplishing this by placing the javascripts in question to /public and appending them using jQuery:
Meteor.startup(function() {
$('head').append('<script src="/template_stuff.js"></script>');
// .. all 7 scripts or so
});
If the required scripts are placed in /client/js/, it appears that they either run too early or before the DOM is done loading. If they are placed directly in the header of the main html file, they seem to bug out the same way.
Am I missing something here - is there a more elegant way to make the scripts load after DOM has loaded?
There are several methods for waiting until the DOM has loaded to inject your scripts:
Meteor.startup (as you illustrated)
jQuery's document ready: $(function () { ... })
script tag at the bottom of your layout template
Regarding elegance, I use a handlebars helper as it allows me to consolidate all 'after body' code in one place. Then use jQuery's document ready as needed.
For example,
// in client/handlebars.js
Handlebars.registerHelper('afterBody', function(name, options) {
function isMobileSafari() {
return navigator.userAgent.match(/(iPod|iPhone|iPad)/) &&
navigator.userAgent.match(/AppleWebKit/)
}
if (isMobileSafari()) {
$(function () {
FastClick.attach(document.body)
})
}
//// load scripts however you want...
// $(function () {
// $.getScript as Daniel suggests
// $('body').append as you have
// pure js: document.createElement('script')
// })
})
// in client/main.html (using mini-pages router)
<template name="layout_no_header">
{{{yield}}}
{{afterBody}}
</template>
<template name="layout">
{{> header}}
{{{yield}}}
{{afterBody}}
</template>
Use jQuery getScript()
(function ($) {
$.getScript("/template_stuff.js");
})(jQuery);
I think you might have a different problem. It sounds like the 3rd party code is being wrapped in a closure and is not working correctly. Try and place them inside the client/compatibility folder. This will prevent it from being wrapped in a closure, which can solve 3rd party problems. Be sure the load order inside of here is correct, it loads files in alphabetical order inside of a folder. (load order)
If that doesn't work, find out where the code is being executed and comment out the call. Then load your html inside of a template, even if it's just a 'page' template with all the html. Create a js file for the template and call the methods in the templates rendered callback.
// Execute this function when myPage template is rendered
Template.myPage.rendered = function() {
stuff.init();
stuff2.run();
};
Note, the to call stuff2, etc.. you'll likely need to have it's script in the compatibility folder so you can reach the namespaces from the template_stuff.js.

Can I build a jQuery prototype function without checking if jQuery has fully loaded?

Can I create a jQuery prototype in this way:
<script
src="/Scripts/jquery/jquery-1.8.2.min.js"
type="text/javascript">
</script>
<script src="/Scripts/common/layout/jquery.fn.buildTableOfContent.js"></script>
When the buildTableOfContents.js file looks like this:
$.fn.buildTableOfContent = function () {
"use strict";
var h2 = this.find('h2');
if (h2.length > 0) {
.....
What I am concerned about is does the prototype function build depend on jQuery
being completely loaded and with my file like this will the buildTableOfContents file
always get executed after the jQuery has fully loaded?
Yes, your code requires $ to be defined, and hence jQuery to be loaded. And yes, putting the jQuery include before your plugin include guarantees that jQuery will be available when your plugin is created.
Upon reaching a script block, the browser will halt any further rendering of the page, download the external document, execute the code inside it, before proceeding to render the rest of the page. This is why you want to put your script blocks just before closing the body (so that the rest of the page will render regardless of blocking requests).

Categories