I've been working hard to achieve 100/100 on Google Pagespeed (https://developers.google.com/speed/pagespeed/insights/) but I keep getting hungup when trying to use Javascript to download CDN based files. I get 'CAUTION: Provisional headers are shown.' and I assume it's blocking this kind of call for security reasons but I'm stuck.
I can call script files asycn like this, which Google likes:
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js" async></script>
But what am I to do about the CSS files? If I call it the normal way:
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
Google complains and says I have a 'blocking CSS resources'.
Here's a copy of the code I've been trying to use so far:
var headID = document.getElementsByTagName("head")[0];
var cssNode = document.createElement('link');
cssNode.type = 'text/css';
cssNode.rel = 'stylesheet';
cssNode.href = '//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css';
headID.appendChild(cssNode);
Anyone have any suggestions?
Here is the code I ended up creating to handle loading both css and js files async at the same time. Just put this at the bottom of your HTML before you closing tag and edit the loadjscssfile() calls as necessary.
<script>
/* Beginning of async download code. */
window.onload = function(){
function loadjscssfile(filename, filetype) {
if(filetype == "js") {
var cssNode = document.createElement('script');
cssNode.setAttribute("type", "text/javascript");
cssNode.setAttribute("src", filename);
} else if(filetype == "css") {
var cssNode = document.createElement("link");
cssNode.setAttribute("rel", "stylesheet");
cssNode.setAttribute("type", "text/css");
cssNode.setAttribute("href", filename);
}
if(typeof cssNode != "undefined")
document.getElementsByTagName("head")[0].appendChild(cssNode);
}
loadjscssfile("//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css", "css");
loadjscssfile("//fonts.googleapis.com/css?family=Open+Sans:300&subset=latin,cyrillic-ext,latin-ext,cyrillic,greek-ext,greek,vietnamese", "css");
loadjscssfile("/css/style.css", "css");
loadjscssfile("//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js", "js");
loadjscssfile("//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js", "js");
};
/* End of async download code. */
</script>
Google provides a good explanation of this here:
https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery
It seems like they want you to inline CSS that is crucial to the page's initial display. Then load secondary CSS after. Since the bootstrap CSS is one large mix, I think it'll be non-trivial to separate crucial/non-crucial for your page.
Perhaps you can inline some duplicate CSS that exists in bootstrap.css
I'd suggest you to inline the styles for the critical path (above the fold):
https://github.com/addyosmani/above-the-fold-css-tools
https://github.com/addyosmani/critical
Then load the other css async:
https://github.com/filamentgroup/loadCSS/
Related
I am using font-awesome 5.3.1 css and adding to my page like this:
<script type="text/javascript">
var shared = {};
shared.css = ["https://use.fontawesome.com/releases/v5.3.1/css/all.css"];
(function () {
var container = document.querySelector('head');
shared.css.forEach(function (href) {
var css = document.createElement('link');
css.href = href;
css.rel = 'stylesheet';
css.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(css);
});
})();
</script>
This is at the very end of the body yet when I run my page on PageSpeed it complains of the render blocking css related to font awesome.
How do I fix this? I tried font awesome cdn but it only support 4.7 and there is no new version on it.
You can execute this Javascript after the page loads completely. See follow:
document.addEventListener('DOMContentLoaded' (){
/*** Put the code here **/
})
This will ensure that script is loaded once the Document is loaded fully.
I have the following code that is run after the page loads:
function prepare_bootstrap()
{
console.log("Preparing bootstrap...");
var items = document.body.getElementsByTagName("*");
for (var i = items.length; i--;)
{
items[i].style.cssText = '!important';
}
var style1 = document.createElement("link");
style1.rel = "stylesheet";
style1.href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css";
document.body.appendChild(style1);
var style2 = document.createElement("link");
style2.rel = "stylesheet";
style2.href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css";
document.body.appendChild(style2);
var script = document.createElement("script");
script.src = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js";
script.onload = script.onreadystatechange = function()
{
if (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")
{
prepare_bootstrapdialog();
}
};
document.body.appendChild(script);
}
I am loading bootstrap to allow for nicely formatted popups.
However, the bootstrap overrides almost everything, making the page look messed up.
I tried making every style important, but to be honest, I have no idea what I'm doing.
Is there any way to make css NOT override previous css?
Thanks!
EDIT: As before stated, it is impossible to load bootstrap first as the javascript is part of a bookmarklet.
Have a read of this:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#A_scoped_stylesheet
Will only work in some browsers though, so not an option for an publicly facing site. The best way to do this really would be to not just include bootstrap in its entirety, instead picking out the bits you need and just using them. You can get custom builds of bootstrap here:
http://getbootstrap.com/customize/
You really shouldn't just load css styles that you aren't actively using. Doing so creates more debugging work for you if you encounter styling issues, and also means you're potentially wasting bandwidth.
You should load the bootstrap stylesheet first, that way any stylesheets linked after that will override bootstrap's
(This is making use of the cascading part of CSS)
See https://stackoverflow.com/a/964343/4206206
I'm new to Javascript (but not HTML or CSS) and am trying to use this script by Lalit Patel to detect whether a font is installed on the user's system, and if not, to serve a modified style sheet. I've been successful in getting the first part to work: I uploaded the fontdetect.js file, called it in my header, then pasted this before the end of my body tag:
<script type="text/javascript">
window.onload = function() {
var detective = new Detector();
alert(detective.detect('Courier'));
};
</script>
(With Courier used as an example.) This causes an alert to pop up on page load telling me whether a font is installed, and works beautifully. But I don't know how to get the script to, instead of triggering an alert, grab a different stylesheet. This seems like basic stuff but I just can't find the specifics anywhere, even though I've been plowing through Javascript tutorials. Any guidance would be greatly appreciated!
If any more specifics are needed: If a user doesn't have the custom font installed or has custom fonts turned off entirely, I'd like to, using CSS change the size/spacing properties of the text so that the fallback font is able to fit in the same space.
var detective = new Detector();
if (!detective.detect('Courier')){
var s = document.createElement('link');
s.rel = 'stylesheet';
s.type = 'text/css';
s.media = 'all';
s.href = '/link/to/alternative/stylesheet.css';
document.getElementsByTagName('head')[0].appendChild(s);
}
Guessing something like that. if .detect() fails, it will dynamically create and insert a stylesheet in to the page. If you encounter problems, you can also use .setAttribute() off of the s element.
You can use JS to add a stylesheet to the website:
var detective = new Detector();
if (!detective.detect('Courier')){
document.createStyleSheet('location/stylesheet.css');
}
else
{
document.createStyleSheet('location/otherstylesheet.css');
}
So you could do a check to see if the Dectector returns true, if not load one style, if it does then load the other.
After trying all the methods presented, this is the one that worked for me (from this answer, but it's really a mix of the two answers presented here). It should be noted that this works in IE8, unlike most of the other methods (sadly I do need IE8 compatibility).
<script type="text/javascript">
window.onload = function() {
var detective = new Detector();
if (!detective.detect('Meat')){
var url = 'link/to/style.css'
if(document.createStyleSheet) {
try { document.createStyleSheet(url); } catch (e) { }
}
else {
var css;
css = document.createElement('link');
css.rel = 'stylesheet';
css.type = 'text/css';
css.media = "all";
css.href = url;
document.getElementsByTagName("head")[0].appendChild(css);
}
}
};
</script>
This detected that my browser wasn't accepting embedded fonts ("Meat" being one of the fonts I embedded) and served up the alternate CSS (although with a slight delay/flash of unstyled text, maybe because it's at the bottom of the page?) Anyhow, thanks again for all the help!
I want to dynamically insert some HTML content and some CSS urls through JS.
I have 3+ CSS files. I want them to be downloaded before my content is inserted on the page.
Is there a way to find out whether the above-mentioned files have been downloaded?
This is how it should work:
Download css files;
Show HTML after all the css files have been downloaded;
Start loading JS files after inserting the HTML;
Trigger callback after all the JS files have been loaded;
You could use YepNope.js, YepNope allows you to build asynchronous conditional tests to see whether resources have loaded. Once your tests have passed you can tell it to inject new CSS or JS files.
Example below has been taken from the YepNope.js site.
yepnope.injectCss( stylesheetSource [, callback ] [, elemAttributes ] [, timeout ]);
// Example
yepnope.injectCss("print.css", function () {
console.log("css injected!");
}, {
media: "print"
}, 5000);
You can even make YepNope load the initial CSS files first and then once they have completed loading YepNope can trigger a callback to do additional tasks, such as loading more JS or CSS files.
Source: https://stackoverflow.com/a/3794242/1292652
Retreiving CSS text using AJAX
Instead of using messy workarounds to determine of the CSS has loaded, you can use AJAX function to create a dynamic CSS URL and fetch the it as plain text.
Inserting the CSS
After fetching the raw text, you can use this function to insert the CSS into a style tag and add a callback:
function loadCss(cssText, callback) {
var style = document.createElement('style');
style.type='text/css';
if(callBack != undefined){
style.onload = function() {
callback();
};
}
style.innerHTML = cssText;
head.appendChild(style);
}
Using it
And now you can use this as:
loadCss(ajaxResponseText, function(){
console.log("CSS loaded, you can show the dialog now :)");
})
Allowing cross-domain AJAX
In your comment, you mentioned you had to load jQuery and jQueryUI which I'm guessing wil de on a different domain.
To get around the AJAX cross-domain restriction, check out this link or this one or this library
<html>
<head>
<!--
Put your styles here. Make sure the *last* CSS file has the following:
body { display: block !important }
This will allow the stylesheets to load while the body is hidden.
When the last stylesheet is loaded, the body will be visible.
-->
</head>
<body style="display: none">
<!-- Lots of HTML here -->
<!--
Put your script tags here.
In the first script, I recommend binding to the window.load event...
which will be fired once all your scripts are done loading.
-->
</body>
</html>
I think this is somewhat along the lines of what you wanted:
<html>
<head>
<!-- CSS Stuff goes here -->
<!-- JS Stuff come right after -->
</head>
<body>
<!-- HTML Stuff comes withing Body -->
</body>
</html>
This will cause:-
All the CSS files to download in parallel;
Then the HTML stuff gets parsed and displayed;
The browser gets all the JS;
The JS is tun and it does whatever inserting it wants;
Your question is a bit confusing, can you clarify it? I didn't understand why you'd require to have such an order, can you give the big picture? JS is meant to be a fire-and-forget language, it will be awkard to see if everything's downloaded. Did this make sense to you and/or help you?
I know it is not a pretty solution, but you can fetch the CSS files using AJAX and add their content to a <style>, instead of inserting a <link> to the DOM.
Using AJAX, you can know for sure when the CSS is completely downloaded and add the whole thing into the page under your terms.
Source: https://stackoverflow.com/a/8122005/1292652
I don't recommend this for the same reason why you shouldn't use a while loop to implement sleep(), but if you must, you can try it.
In the linked answer, a reference style was added. For example: #ensure-cssload-8473649 { display: none }. But since you're downloading files not in your control, you'll have to select a few styles from the sheet for detection.
The cssLoad() function is:
var onCssLoad = function (options, callback) {
var body = $("body");
var div = document.createElement(constants.TAG_DIV);
for (var key in options) {
if (options.hasOwnProperty(key)) {
if (key.toLowerCase() === "css") {
continue;
}
div[key] = options[key];
}
}
var css = options.css;
if (css) {
body.appendChild(div);
var handle = -1;
handle = window.setInterval(function () {
var match = true;
for (var key in css) {
if (css.hasOwnProperty(key)) {
match = match && utils.getStyle(div, key) === css[key];
}
}
if (match === true) {
window.clearTimeout(handle);
body.removeChild(div);
callback();
}
}, 100);
}
}
You can use it as:
onCssLoad({
"id": <insert element CSS applies to>,
css: <insert sample CSS styles>
}, function () {
console.log("CSS loaded, you can show the dialog now :)");
});
Why don't you add an element on page and check some style that it should have with some js. If it has that style then that stylesheet is probably load.
Like this:
if (document.getElementById("csspage-3-box").style.color === "#somethingUnique") {
//it is loaded
}
You could append a few hidden divs with IDs unique to each style sheet. If they have any styles at all then you know that the sheet you want is loaded. This operation is cheap. It does add some non-semantic div elements though. That being said, I feel like everyone has at least one non-semantic element in their DOM.
Just a suggestion, but 3 css files isn't that many. Other easy solutions include the following:
A) You could just cat and minify them and deliver them all at once.
B) Throw them onto a CDN (like aws s3 + cloudfront), which is super cheap and they will get loaded in parallel.
C) Just load them in as normal. You could also put them on a sub domain.
Now that aside if I were you I'd divide my CSS down into a bunch of files. 3 is too few files. I'd separate out grid and every element type. Then re-assemble them on a per page basis (or page group). This allows you to include each individual piece as you need.
After all that it is fairly easily to append a link tag (as long as you can easily get a path to it). I recommend using the link instead of the style tag because if you do go the CDN route or subdomain route you will have issues with cross-domain issues if trying to grab the content of the css file with some xhr.
In building the apps that our team develops there can be many CSS & Script files and the head of your HTML can end up looking rather cumbersome. I built a little script that will load all of your files by passing them to the method as an Array. However, I have the loadCSS() in its own script file and I just invoke and pass in the Array in via a script tag in the HTML. Code is in monolithic format to make it easier to view.
window.onload = function() {//Use $(document).ready() if using jQuery
var cssArray = ["Style01.css", "Style02.css", "Style03.css"];
loadCSS(cssArray, function () {
alert("Style sheets have been loaded.");
});
//Note: You can do a similar process for the JS files.
//Load HTML when CSS is finished - Shown below
loadHTML();
}
function loadCSS(array, callback) {
var cssArray = array;
for (var i = 0; i < cssArray.length; i++)
{
document.write("<link rel='stylesheet' type='text/css' href='styles/" + cssArray[i] + "' />");
//console.log(cssArray[i] + " style sheet has been loaded.");
//Detects when for loop is finished evaluating the Array
if (i == cssArray.length - 1) {
return callback();
}
}
//loadHTML() can also be invoked here to ensure CSS files have been loaded.
}
If you are attempting to build out some HTML dynamically then you could try something like this after the CSS files are loaded.
function loadHTML() {
//Create Main Table
var mainTable = document.createElement("table");
mainTable.id = "mainTable";
mainTable.cellPadding = 0;
mainTable.cellSpacing = 0;
mainTable.border = 0;
//Create Body
var mainBody = document.createElement("tbody");
//Create Table Row
var mainTR = document.createElement("tr");
mainTR.style.height = "50px";
mainTR.className = ""; //Insert class from one of the style sheets
//Create Main Cell
var mainTD = document.createElement("td");
mainTD.id = "mainTD";
//mainTD.style.width = "";
mainTD.style.textAlign = "left";
mainTD.style.padding = "5px 10px 5px 10px";
mainTD.className = ""; //Insert class from one of the style sheets
mainTD.innerHTML = "Test Text";
mainTR.appendChild(mainTD);
mainBody.appendChild(mainTR);
mainTable.appendChild(mainBody);
document.body.appendChild(mainTable);
}
If you have any questions please feel free to ask. Although there are already some good examples posted, hopefully this can help.
what about :
$(document).ready(function() {
$("body").hide();
});
then, in the downloaded css files :
body { diaplay:block; }
Use loadCSS to load your CSS. and inside onload method put your code you want to execute after all css load.
var counter = 0;
function onload(){
counter--;
setTimeout(function(){ // insuring if loaded immediately
if(counter == 0){
// your code here //******** CODEREPLACE ************/
alert('all css files loaded!');
}
});
}
function loadCSS(href){
counter++;
l = document.createElement('link');
l.href = href;
l.onreadystatechange = function () { // For IE
if (this.readyState == 'loaded'){
onload.call( this );
}
};
l.rel = 'stylesheet';
l.type='text/css';
l.onload = onload;
document.getElementsByTagName('head')[0].appendChild(l);
}
loadCSS('http://www.google.co.in/search?q=load+css+files+dynamically&aq=f&oq=load+css+files+dynamically&sugexp=chrome,mod=1&sourceid=chrome&ie=UTF-8');
I have the following file:
<html>
<head>
<title></title>
<link rel="css" type="text/css" href="/empty.css" title="css" />
<script type="text/javascript" src="/Prototype"></script>
<script type="text/javascript">
function load_content()
{
var d = new Date();
new Ajax.PeriodicalUpdater('content', '/DOC?'+d.getTime(),
{
method: 'post',
frequency: 5,
onSuccess: function(transport) {
for(i=0; (a = document.getElementsByTagName('link')[i]); i++)
{
if(a.getAttribute('rel') == 'css' && a.getAttribute("type") == 'text/css')
{
a.href = '/CSS?'+d.getTime();
}
}
}
});
}
</script>
</head>
<body>
<div id="content"></div>
<script type="text/javascript">
load_content();
</script>
</body>
</html>
Note: Ignore the d.getTime() calls...these are just to get around an issue with IE not loading a new page from an AJAX call because it's caching scheme is too aggressive.
Basically, when it reloads the file at /DOC, it is supposed to be setting the current stylesheet to the file at /CSS... both DOC and CSS and constantly changing.
What's weird is that in Chrome it works great. DOC loads up in the "content" div and the stylesheet gets set to CSS and that css is applied to the page. I can change with CSS page and withing 5 seconds, when the page is refreshed, the CSS will be refreshed as well.
But in IE and Firefox, the HTML will load and I can see that the href attribute of the stylesheet link IS getting changed to "/CSS + getTime()" but, while the HTML loads, the css is never applied to the page. I can even change the content of DOC and it updates, but the css is never even applied. It just stays a style-free page.
Does Firefox and IE not support changing the style sheet reference in this way?
Is there a better way to do this?
Rather than changing the sheet in a single link, try using alternate style sheets. See this link on using alternate style sheets:
http://www.alistapart.com/articles/alternate/
The best way to include files via javascript is to insert a new dom element.
var a = document.createElement('link');
a.href="inset.css";
a.rel="stylesheet";
document.getElementsByTagName("head")[0].appendChild(a);
However, obviously the problem you're going to run into though is that firefox and ie will not repaint the canvas once the document is finished loading (and you're using ajax). The way you get around that is by taking the contents of the stylesheets and including them in a style element. This sample code will change the color on the page dynamically.
function onLoadFunction() {
var a = document.createElement('style');
a.appendChild(document.createTextNode('body {color: blue;}'));
document.getElementsByTagName("body")[0].appendChild(a);
}
When you load a new sheet, just destroy the css inside the style element and replace it.
maybe this will help you ...
http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
function loadjscssfile(filename, filetype){
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", filename)
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
}
It looks like you are simply reloading the existing page every time. Why not just use the refresh tag in your header to force the document to reload each time and put in the CSS and content server-side. A lot simpler and it works even with javascript disabled.
<meta http-equiv="refresh" content="5;url=http://example.com/DOC" />
It might be a caching issue. If you do a hard refresh (Ctrl+R in FF, Ctrl+F5 in IE) does it display the style properly? If that does fix it, you may want to look at removing the Last-Modified header from the CSS file or adding a cache control header telling the browser not to cache it.