I used to build my websites based on the HTML5 Boilerplate: styles and modenizr in the head, jQuery (google CDN or hosted file) and scripts before the closing body tag. Something like that:
<!DOCTYPE html>
<!-- modernizr conditional comments here -->
<html class="no-js">
<head>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.10.2.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Now I want to remove all render-blocking below-the-fold css and js as suggested by Googles PageSpeed Insight.
How do I defer the css and js files including the jQuery library loaded from google?
What should I do about modernizr?
To remove this particular warning you need to do the following:
defer the load of all external CSS until after page onload
defer the load of all external JS until after page onload
In practice that means you need to do the following:
split your CSS into that required to avoid the "flash of unstyled content" (FOUC) and the rest
split your javascript likewise
inline the CSS and JS that is required
defer the load of the other CSS and JS until after page onload.
Using build tools is the only sane way of doing this. You can do it with various Grunt tools, or with the ant-based H5BP Build Script.
The basic method of deferring loads is as follows:
(function () {
// Load jQuery after page onload
function loadJS() {
var url = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js";
var n = document.createElement("script");
n.src = url;
n.onload = loadJS1;
document.body.appendChild(n);
}
// Load some JS after jquery has been loaded.
function loadJS1() {
var url = "js/main.js";
var n = document.createElement("script");
n.src = url;
// Continue chaining loads if needed.
//n.onload = loadJS2;
document.body.appendChild(n);
}
// Check for browser support of event handling capability
if (window.addEventListener) {
window.addEventListener("load", loadJS, false);
} else if (window.attachEvent) {
window.attachEvent("onload", loadJS);
} else {
window.onload = loadJS;
}
})();
Related
As a test to learn more about optimizing websites, I've been trying to get my site to have the perfect score on PageSpeed Insights. Everything is going great so far except for the CSS delivery.
I did manage to get a perfect result by using the preload tag, but for some reason that didn't load for Firefox. So I tried using other solutions.
I switched then over to this:
<link rel="stylesheet" href="~/css/site.min.css" media="none" onload="if(media !== 'all')media='all';">
<noscript><link rel="stylesheet" href="~/css/site.min.css"></noscript>
Which seemed very effective but pagespeed doesn't pick it up so it only gives me a 85 score rating.
Same happened when I used <link rel="stylesheet" href="~/css/site.min.css" media="none"/> in head and <link rel="stylesheet" href="~/css/site.min.css"> at the end of my body.
Then I tried loading my css with Javascript like this:
<noscript id="deferred-styles">
<link rel="stylesheet" href="~/css/site.min.css"/>
</noscript>
<script>
var loadDeferredStyles = function() {
var addStylesNode = document.getElementById("deferred-styles");
var replacement = document.createElement("div");
replacement.innerHTML = addStylesNode.textContent;
document.body.appendChild(replacement);
addStylesNode.parentElement.removeChild(addStylesNode);
};
var raf = requestAnimationFrame ||
mozRequestAnimationFrame ||
webkitRequestAnimationFrame ||
msRequestAnimationFrame;
if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
else window.addEventListener('load', loadDeferredStyles);
</script>
But this also had the same effect! Pagespeed didn't pick it up and gave me a bad rating. Any reason why for this? Because the above code can be found on their site!
according to my experience with google pagespeed, for optimizing css delivery you have to write inline css code of first fold of your webpage.So that it can be painted fast, and rest of the css you can write in the external file. Minimize and concat css files used in the page. Refer this link for more info optimize css delivery
This is what I use just before the </body> and works fine.
<script type="text/javascript">
function downloadAtOnload() {
// Dynamically load CSS
var ls = document.createElement("link");
ls.rel="stylesheet";
ls.href= "css/my-css-file.css";
document.getElementsByTagName("head")[0].appendChild(ls);
}
if (window.addEventListener) window.addEventListener("load", downloadAtOnload, false);
else if (window.attachEvent) window.attachEvent("onload", downloadAtOnload);
else window.onload = downloadAtOnload;
</script>
In the index.html page I put a .gif before all the tags that laod the files. Then, with JQuery, I removed that loader. But it didn't work. A blank page was shown, because it was waiting for load some .js files.
So, I created a script that load the files dynamically.
This is the html code:
<!doctype html>
<html ng-app="myApp">
<head>
...here I'm loading all css files
</head>
<body>
<!-- This is the loader -->
<div id="loader" style="background:url('URL_IN_BASE_64')"></div>
<div ng-cloak ng-controller="MyAppController">
<div ui-view></div>
</div>
<script>
(function () {
var fileList = [
"vendor/angular/angular.min.js",
"vendor/jquery/jquery.min.js",
"vendor/angular-aria/angular-aria.min.js",
....
]
function loadScripts(index) {
return function () {
var e = document.createElement('script');
e.src = fileList[index];
document.body.appendChild(e);
if (index + 1 < fileList.length) {
e.onload = loadScripts(index + 1)
} else {
var loader = document.getElementById("loader");
if (loader) {
loader.remove();
}
}
}
}
loadScripts(0)();
})();
</script>
</body>
</html>
Now, the problem is that the page first loads all the css files, then angular.min.js, then my gif, and then the other files. Why? How can I immediatly load the gif and, suddently, all other files?
Thank you!
The html parser is being blocked while waiting for your css and js files to load. The work around is to load your stylesheets and script files asynchronously. Do the following for all files that you load in your <head>, including table.min.css, ng-img-crop.css, and angular.min.css.
For css:
<link rel="stylesheet" href="css/styles.css" media="wait" onload="if(media!='all')media='all'">
<noscript><link rel="stylesheet" href="css/styles.css"></noscript>
For js:
<script async src="js/main.js"></script>
Fun fact: Google will actually punish web pages that block the parser from rendering elements while waiting for resources.
Try this
<!--Put this in head tag-->
<link rel="preload" href="link-to-image">
...
<img src="link-to-image">
Because my webpages are a bit heavy, I decided to use a preloader. The purpose of a preloader is to show some content before the main content loads, engaging the user from the start. Therefore it is very important to show preloader ASAP.
There is a slight problem though. Browser will typically wait for all CSS to be loaded before attempting to display and render HTML. This can be problematic if the document contains several biggish stylesheets.
So, my solution was thus:
<!-- In the head: -->
<noscript>
<link rel="stylesheet" href="/big-css1.css">
<link rel="stylesheet" href="/big-css2.css">
<link rel="stylesheet" href="/big-css3.css">
</noscript>
<script>
function LoadCSS(path) {
var head = document.getElementsByTagName('head')[0],
link = document.createElement('link');
link.setAttribute('href', path);
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
head.appendChild(link);
}
</script>
<style>
/*preloader CSS is here*/
</style>
...
</head>
<body>
<div id="preloader">
Some nice content here
</div>
<!-- Main content starts below: -->
<div id="wrapper">
<script>
//hide main content until its loaded
//use JS and not CSS to support those with JS disabled
var el_preloader = document.getElementById("preloader");
var el_wrapper = document.getElementById("wrapper");
el_preloader.style.display = "block";
el_wrapper.style.display = "none";
LoadCSS('/big-css1.css');
LoadCSS('/big-css2.css');
LoadCSS('/big-css3.css');
</script>
...
<!-- After jQuery has been loaded -->
<script>
jQuery(window).load(function() {
document.getElementById("wrapper").style.display = "block";
jQuery('#preloader').fadeOut(1000, function () {
jQuery('#preloader').remove();
});
});
</script>
So, basically, if the use supports JavaScript, then JavaScript handles loading of stylesheets, and if JS is turned off, then the stuff inside the <noscript> tag is parsed and thus stylesheets are loaded normally.
This setup works fine in modern browsers, however I am not sure what impact it will have on SEO, considering Google and others are now evaluating user experience on the websites, and for that they need to parse CSS. Are they smart enough to render the website correctly with this solution? ARe there any adverse impacts on SEO with preloaders in general?
Thanks.
For javascripts hosted on CDN, I can always add some scripts below it to check if it is loaded successfully:
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
if (!window.jQuery) {
var script = document.createElement('script');
script.src = "/js/jquery.min.js";
document.body.appendChild(script);
}
</script>
I also used some hosted css files, like bootstrap. How can I reiliably check if it is loaded on the web page, and provide a fail-safe if isn't?
-----EDIT--------
And, by the way, should I use:
document.body.appendChild(script);
or:
document.write("<scr"+"ipt>"...);
Which one guarantees execution order in all browsers?
you could use onload and onerror events...
<link rel="stylesheet" type="text/css" href="cdnurl/styles.css"
onload="alert('loaded')" onerror="javascript:loadCSSBackup()" />
javascript:
function loadCSSBackup(){
var css = document.createElement("link")
css.setAttribute("rel", "stylesheet");
css.setAttribute("type", "text/css");
css.setAttribute("href", 'backup/styles.css');
document.getElementsByTagName("head")[0].appendChild(css);
}
regarding 2nd question, you should avoid using document.write. Appending to body or head as in the code above should be fine.
In my project requirement I need to add java script dynamically
I have a js file global.js which contains all global variable that I want to add dynamically.
I also want to add global.js before util.js( which uses variable defined in global.js) which is not happening
If I open page source I can't see the global.js added there
I also want this to be work in all browser mainly in ie6 and ie8
my code Index.html
<script src="../fw.ui.lib/resources/sap-ui-core.js" id="sap-ui-bootstrap"
data-sap-ui-libs="sap.ui.commons, sap.ui.table, sap.ui.ux3"
data-sap-ui-theme="sap_goldreflection">
</script>
<script src="../fw.ui/js/application_properties.js" type="text/javascript"></script>
<script src="../fw.ui/js/header.js" type="text/javascript"></script>
<script src="../fw.ui/js/dao/userDAO.js" type="text/javascript"></script>
<!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->
<script src="js/controls/DynamicJsLoad.js" type="text/javascript"></script>
<script></script>
<script>addScriptDynamically()</script>
<script src="js/controls/util.js" type="text/javascript"></script>
DynamicJsLoad.js
function loadScript(url){
var e = document.getElementsByTagName("script")[5];
var d = document.createElement('script');
d.src = url;
d.type = 'text/javascript';
d.async = false;
d.defer = true;
e.parentNode.insertBefore(d,e);
}
function addScriptDynamically(){
var scheme = getCacheBurstScheme();
if( scheme == 'dev'){
scheme = new Date();
}
loadScript('js/global.js'+'?scheme='+scheme);
}
If you are using jQuery anyway, you could leverage their loadScript() function. An example from their website:
$.getScript("ajax/test.js", function(data, textStatus, jqxhr) {
// [...]
});
Regarding your current solution, here are a couple of hints:
You set defer = true, which will defer loading (and executing) of your dynamically loaded script to the end. Thus after util.js. I don't think you want that
Dynamically loaded js sources do not appear in your page's source code. You might need to use a tool like Firebug to validate if things have loaded.