Can Bootstrap be loaded asynchronously? - javascript

I have not used Bootstrap for very long and am unsure of whether it needs to be loaded non-asynchronously in the <head> for page-building purposes.
Google suggests using this code to load JS files asynchronously:
<script type="text/javascript">
// Add a script element as a child of the body
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "deferredfunctions.js";
document.body.appendChild(element);
}
// Check for browser support of event handling capability
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
Can I load bootstrap.min.js in this fashion, or should I load it non-asynchronously?

Bootstrap.js requires jquery to run. If you are looking to get the benefit of loading async script, then you would probably want to load jquery (and potentially other libraries) async as well... The problem with this is that you would have no guarantee that jquery async finished before the bootstrap using the example code above. I'm also sure you have your own javascript that you want to write to use the bootstrap.js features. This means even more dependencies. You could write logic to wire up dependencies and async load manually, but this would become a lot of work as the number of scripts you might need to include increase.
Requirejs is a library that takes care of all this dependency management for you, and can load your files asynchronously (and in the correct order). Another benefit of this library is the optimizer which can trace dependencies and "burn" them into a single (optionally minified) file. After you use the optimizer to optimize your "main" js file (the one with all the dependencies you need for the page), requireJS can just load that file asynchronously. Only need one script include!
An example would look like:
/app/main.js:
requirejs.config({
paths: {
jquery: "lib/jquery-1.11.0",
bootstrap: "lib/bootstrap"
},
shim: {
bootstrap: {
deps: ['jquery']
}
}
});
//Define dependencies and pass a callback when dependencies have been loaded
require(["jquery", "bootstrap"], function ($) {
//Bootstrap and jquery are ready to use here
//Access jquery and bootstrap plugins with $ variable
});
jquery.js and bootstrap.js would live under /app/lib in this case (along with require.js).
In your HTML you would have this script tag:
<script src="/app/lib/require.js" data-main="/app/main"></script>
This would load in bootstrap and jquery (in the correct order) and then pass those modules as parameter(s) (only jquery/$ is needed since bootstrap is just a plugin on top of jquery) to your callback function.

So taking Patrick's excellent answer (which helped me a lot), if you run your site against https://developers.google.com/speed/pagespeed/insights/ it will still give you a warning that
Eliminate render-blocking JavaScript and CSS in above-the-fold content
Your page has 1 blocking script resources
Remove render-blocking JavaScript:
http://mydomain/js/require.js
But taking the OP setup (and the one Google recommends) you can do this just before the </body> tag
<script type="text/javascript">
function requireJSOnload() {
var element = document.createElement("script");
element.src = "/js/require.js";
element.setAttribute('data-main', '/js/main');
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load", requireJSOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", requireJSOnload);
else window.onload = requireJSOnload;
</script>
Now when you run against https://developers.google.com/speed/pagespeed/insights/ you will have no JS blocking scripts.

Starting from Bootstrap 5, the JavaScript file can be loaded in async directly.
While Patrick's answer is correct for Bootstrap 4 & below, Bootstrap 5 removes the jQuery dependency which allows for the full script to be loaded in both async or defer.

Related

Load Multiple js file after page load

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.

Javascript Deferred Functions

I've been looking around at JavaScript to try and get some code to load asynchronously
I found this code sample
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "defer.js";
document.body.appendChild(element);
}
if (window.addEventListener) {
window.addEventListener("load", downloadJSAtOnload, false);
}
else if (window.attachEvent) {
window.attachEvent("onload", downloadJSAtOnload);
}
else {
window.onload = downloadJSAtOnload;
}
</script>
from http://www.feedthebot.com/pagespeed/defer-loading-javascript.html
The thing is it doesn't quite do what I'm trying to achieve. I would like to be able to do something similar but setup a deferred or some type of function that is called after this has all loaded. Is that possible? If so can anyone help explain how?
You can try to use the onload event of the script tags you are loading. See this question, for example: Trying to fire the onload event on script tag. However, this mechanism seems pretty sketchy and may not be cross-browser.
Another possible approach is to have the script that is being loaded trigger an event that can be handled by the existing javascript on the page. This may or may not make sense for your particular case, and it requires you to have control over the loaded script.
Finally, these days it's rare for javascript loading to be a performance bottleneck for your website. So why are you trying to dynamically load javascript? Could you solve the same problem by loading some other resource, e.g. by doing an AJAX request?
You've tagged jQuery on your question. It has $.getScript() which does exactly what you're asking for in a purely cross browser fashion. It will load the script asynchronously and then call the specified callback when the script has finished loading and initializing:
$.getScript("defer.js", function() {
// script loaded here
// you can run code here that uses that script
});
If you really want to wait until the DOM is loaded before loading this script (generally not necessary), you could do this:
$(document).ready(function() {
$.getScript("defer.js", function() {
// script loaded here
// you can run code here that uses that script
});
});
Or, to wait until ALL other resources are loaded including images:
$(window).load(function() {
$.getScript("defer.js", function() {
// script loaded here
// you can run code here that uses that script
});
});
If you're interested in some references on when scripts are loaded (particularly with defer and async attributes, you can read this detailed post.

Loading jQuery if not loaded - failed approach

This is what I have in :
<script src="myscript.js" type="text/javascript"></script>
<script type="text/javascript">testfunction("hello");</script>
Inside myscript.js:
if(!window.jQuery)
{
var script = document.createElement('script');
script.type = "text/javascript";
script.src = 'jquery-1.9.0.min.js';
document.getElementsByTagName('head')[0].appendChild(script);
}
function testfunction(str) {
$(document).ready(function () {
alert(str);
});
}
Of course jQuery is not needed for the current testfunction, but it will be needed. Using this approach, jQuery is downloaded but NOT loaded to the browser when calling to testfunction() (Uncaught ReferenceError: $ is not defined).
What I could do is to load jQuery in a different script before my JS is loaded. In that case, it will work, but I would have three different scripts and that seems to be not elegant in my honest opinion.
Is there any other way to achieve this?
Thanks!
You might want to try looking at this answer. It may not be waiting until jQuery is fully loaded.
With your code, the script will be loaded asynchronously. You need to add to the script tag an onload event that will trigger the parts that are dependent on jQuery.
If you want the script to be loaded in a synchronous way, use document.write:
window.jQuery||document.write(unescape("%3Cscript src='"+jQueryURL+"' type='text/javascript'%3E%3C/script%3E"));
The document.write technique is very common, just keep in mind that you cannot use it after your document has loaded (or else it will overwrite the whole page).
If you want to use dynamic loading on a larger scale, look at existing loader or AMD libraries.

What prevents Require'ed scripts from injecting more script tags

I am trying to use some older scripts in an AMD/Require site. Some of these older scripts inject other scripts through the use of document.write or document.writeln of tags. Below is a simplified form of what's happening:
script1.js:
console.log('script1');
document.writeln("<script src='script2.js'></script>");
script2.js
console.log('script2');
If I load script1 in the classic way, DOM shows both script tags are present and console output shows that both are executed:
<head>
<script src="script1.js"></script>
</head>
<body></body>
BUT If I load script1 via RequireJS, DOM shows only the script1 tag. Console output shows that script1 is executed, but the document.writeln is apparently ignored and thus script2 is not added to the DOM:
<head>
<script src="require-jquery.js"></script>
<script language="javascript">
require( ['script1'], function( ) { } );
</script>
</head>
<body></body>
What prevents this additional script from being inserted into the DOM? I'm suspect I would have other issues with load order even if the above did work correctly. But I want to understand this hole in my knowledge of what RequireJS is doing differently that prevents the additional script from loading.
I am using version 2.1.4 of RequireJS. The behavior is the same on Firefox and Chromium.
EDIT: I forgot to mention that in the real scenario, the path to 'script2' is dynamic based on some server-side logic as the first script is generated.
EDIT2: I obscured something else in my attempt to boil down the scenario: I can't (easily) change these old libraries as they are controlled by a different division of the company and are partially dynamic based on server-side logic (which again, I don't control). So as #ddotsenko suggested, maybe I should rephrase the question: how does one take some legacy scripts that depend on injecting script tags, and make them fit into the rest of the site that is trying to do the right thing by using AMD/RequireJS? The suggested shim approach comes close but doesn't work because the dependencies are unknown.
The problem is document.write() does not work after page load (and RequireJS uses XHR).
But you can use "shim" to predefine dependencies of your old modules - http://requirejs.org/docs/api.html#config-shim
config.js
require.config({
deps: ["main"],
shim: {
script1: {
deps: ['script2'],
exports: "someScript1Object"
}
}
});
main.js
require(
[ "script1" ],
function(someScript1Object) {
// ...
}
);
And console output is
script2
script1
UPDATE. Second variant: override document.write / document.writeln
You can use trick from ControlJS - overriding document.write. Try this code:
require.docwrite = function(text){
var aMatches = text.match(/src='([^']*)/i) ||
text.match(/src="([^"]*)/i) ||
text.match(/src=([^ >]*)/i);
if ( aMatches ) {
var url = aMatches[1];
require([ url ]);
}
}
document.write = require.docwrite;
document.writeln = require.docwrite;

How to load a Javascript file within another Javascript file and execute file 2 before file 1?

I have written a jQuery plugin, say jquery.plugin.js. I want to use this plugin on a large number of sites.
What I want to do is to write a piece of js code at the top of jquery.plugin.js which will load the jquery.main.js and execute it so that $ is available to be used in jquery.plugin.js.
Thanks.
What you probably want to do is create a new tag in js
script = document.createElement('script');
script.src = 'URL-TO-JQUERY';
And then append that element before the first tag.
document.body.insertBefore(document.getElementsByTagName('script')[0], script);
This will most probably work as the script is inserted before any other js. (except in the head)
Yes, just a interval that checks if jQuery exists:
interval = setInterval(check, 100);
function check(){
if($.version){
// Exec script
}
}
This page may be useful for you
http://code.google.com/p/jquery-ajaxq/
with this script you can use files sequentially
Works only if jQuery is loaded already,
$.when($.getScript("path_to_script")).then(function() {
alert("loaded");
})​
Fiddle - http://jsfiddle.net/fLuDd/
Edit
var fileref = document.createElement('script')
fileref.setAttribute("type", "text/javascript")
fileref.setAttribute("src", 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js')
document.getElementsByTagName("head")[0].appendChild(fileref)
var t = setInterval(function() {
if (typeof(jQuery) == 'function') {
alert("jquery loaded");
clearInterval(t);
}
}, 2000);​
Fiddle - http://jsfiddle.net/fLuDd/1/
Just include the jquery.main.js above the jquery.plugin.js. It should work if you place the script tags in this order.
<script src="jquery.main.js"></script>
<script src="jquery.plugin.js"></script>
If you do not have full control over the html or if jQuery might be loaded from other places also, do this to check if it is loaded or not, and load only if needed.
Load from google
if (typeof jQuery == 'undefined') {
document.write('<script type="text/javascript" src="http://www.google.com/jsapi"></script>');
document.write('<script type="text/javascript">');
google.load("jquery", "1.4.2");
document.write('</script>');
}
Load from same server
if (typeof jQuery == 'undefined') {
document.write('<script type="text/javascript" src="path/to/jquery.min.js"></script>');
}
There are some AMD packages that do what you want, like requirejs, but... you'll have to load them first!
You could use a polling technique (load jQuery dynamically and poll until $ exists), not very elegant or efficient.
You could also attach an onload event handler to the dynamic jQuery script, which will trigger your plugin after loading jQuery. Of course such event handlers have cross-browser compatibility issues, so it's easier to write them... with jQuery.
Bottom line: it's a lot of trouble and you're usually better off just writing two script tags.
P.S.: Some of the above techniques are explained in this article:
http://www.stevesouders.com/blog/2008/12/27/coupling-async-scripts/

Categories