Appending Stylesheet via javascript without flickering - javascript

I have a default CSS code for my page. I am injecting a CSS stylesheet file into the bottom of the head that overrides the default one using JavaScript. For some reason when I load the page, I see the default one and then it overrides it with the loaded stylesheet,
What can I do so that flickering will not occur? I thought that if I add a CSS at the end of the head after the default one I won't see the flickering because the content is not loaded yet, but apparently, it is. Any solution for that?
I don't know if flickering is the right word, I just see the default CSS and immediately after I see the page with the overridden CSS. Furthermore, maybe it's relevant, the overridden CSS only overrides some of the elements not all of them.
Here is the code:
<head>
<link href="/Content/app.min.css?ver=17" rel="stylesheet" />
<script>
// dark theme
if (localStorage.getItem("current_theme") === "dark") {
var head = document.head,
link = document.createElement('link');
link.type = 'text/css',
link.rel = 'stylesheet',
link.href = '/Content/dark_theme.min.css?r=' +
(Math.floor(Math.random() * 20000) + 1);
head.appendChild(link);
}
</script>
// the JavaScript appended stylsheet will render here before the </head> element
</head>
What happens is that the change of the new CSS happens only after the page has been loaded. I put a breakpoint in the footer, and only after the page is loaded I see the new CSS update.
I've found out that this happens because the stylesheet file is loaded asynchronously and therefore the delay. I need to inject the CSS code inline to make it work. The problem is with that approach is that it adds 9KB to each page call instead of dynamically based on the localStorage variable value.

The following code works faster:
<head>
<link href="/Content/app.min.css?ver=17" rel="stylesheet">
<script>
// dark theme
if (localStorage.getItem("current_theme") === "dark")
document.head.innerHTML += '<link rel="stylesheet" href="/Content/dark_theme.min.css">';
</script>
</head>
But your approach is wrong.
You'd better use a cookie for this stuff. Save the theme name to the cookie. Read cookie from request headers and include the required css right on the server side. So the client receives:
<head>
<link href="/Content/app.min.css?ver=17" rel="stylesheet">
<link rel="stylesheet" href="/Content/dark_theme.min.css">
</head>
Also I'd recommend setting correct Expires headers on the server and getting rid of ugly ?ver=17 or ?r=(Math.floor(Math.random() * 20000) + 1)

Related

Avoiding flash of unstyled content for javascript-injected css in <head>

I have the following JavaScript in the <head> of a simple static page, which specifies either a.css or b.css as the stylesheet to use depending on the result of some additional JavaScript (useA(), not shown here).
<head>
<script>
var ss = document.createElement("link");
ss.rel = "stylesheet";
ss.href = (useA() ? "a.css" : "b.css");
document.head.appendChild(ss);
</script>
</head>
This results in a flash of unstyled content (FOUC) when I load or refresh the page on both Firefox and Chrome.
Using a plain <link rel="stylesheet" ...> doesn't have this problem: CSS loaded in <head> using <link> is apparently render-blocking. Is there some way to get the same behavior for CSS injected stylesheets as well?
Constraints:
I can't change a.css or b.css so I'm not looking for a solution which involves loading both stylesheets, or a combined styleseet and setting an indicator class on a root element to effect the choice between the sheets. I'm also not looking for visibility:hidden or display:none tricks which delay any display until the page is fully loaded.

Setting href for .less stylesheet through JS

I'm using cookies to find out if the user is in light or dark mode onload of body. I always use the light.less as the fallback/default if a cookie can't be found. So in my <head> I have
<link rel="stylesheet" type="text/css" href="../assets/main.css">
<link rel="stylesheet/less" type="text/css" id="colorMode" href="../assets/light.less">
<script src="../assets/less.js/dist/less.min.js" type="text/javascript"></script>
and my body tag is as follows:
<body onload="checkNav(); checkCookies();" onresize="checkNav()">
The checkCookies() is the function to review the cookie and act accordingly, it is shown below.
function checkCookies() {
var style = getCookie("style");
if (style == 'dark') {
document.getElementById("colorMode").href = "../assets/dark.less";
document.getElementById("switchIcon").innerHTML = "toggle_on";
document.cookie = "style=dark; path=/~sam.walker";
}
else {
//Already set by default
}
}
The getCookie() function simply returns the relevant style cookie
The colorMode stylesheet with href = ../assets/light.less does change as expected to ../assets/dark.less as I've checked with inspector but the style itself does not physically change. I've checked cache and its nothing to do with that. Any help would be greatly appriciated.
Including the stylesheet as a .less format won't work unless you have <script src="less.js" type="text/javascript"></script> added within the <head> section of your page.
Check out the usage information from Less.js here
less.min.js will find references to LESS files and generates CSS from them when it loads.
You are creating a new reference to a LESS file after that, by which time it is too late for less.js to notice.
You need to call checkCookies(); after you have linked to the LESS stylesheet but before you load the less.js script.
Thanks for your help,
Through your info I have found the following (botch) fix:
var elem = document.getElementById('NameOfOriginalLessGeneratedStyle'); //REQUIRES CHANGE ON SERVER CHANGE
elem.parentNode.removeChild(elem);
//Deleted previous styles
less.refresh();
//Loads new style
Should run after stylesheet href change.

Load css file before js

I had a internal style followed by a external style. I noticed the page was blank till the external css didn't load. So i changed it to the below code. Now the external css request is made after the js.
Why for both above and how do i make css request before js. Are there any advantages to it(css before js in below code).
<body>
<style>
body {
background: #333;
}
</style>
<script>
var headHTML = document.getElementsByTagName('head')[0].innerHTML;
headHTML += '<link type="text/css" rel="stylesheet" href="build/main.css">';
document.getElementsByTagName('head')[0].innerHTML = headHTML;
</script>
<!-- The polyfills js is generated during the build process -->
<script src="build/polyfills.js"></script>
Use setTimeout in a function which is called when the body loads.
<body onload="loadPage()">
JS
function loadPage() {
//code to load css
setTimeout(function() {
//code to load js
}, 1);
}
I think that would work. Hope it helps.
This happends because a page is rendered from top to bottom. So I suppose that browser renders you inline styles and goes executing inlines script which uppends link to a style. Then it goes further and executes external script. And after that it loads your newly appended stylesheet.
So, I'd be too naive to ask but why don't you just put your stylesheet link into the head?
Of course you should just put your css files like normal people do :). but if you really want to make it happen, it's gonna get as ugly as this code gets:
<script>
var head = document.getElementsByTagName('head')[0],
link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', 'style.css');
var sheet = "sheet", cssRules = "cssRules";
if (!('sheet' in link)) {
sheet = "styleSheet";
cssRules = "rules";
}
var waitForCSS = setInterval(function() {
if (link[sheet]) {
// now you can load your javascript files here like I did with CSS
// or just use any js code you want.
clearInterval(waitForCSS);
}
}, 10);
head.appendChild(link);
</script>
The way to do this is
<head>
<link type="text/css" rel="stylesheet" href="build/main.css">
<style>
body {
background: #333;
}
</style>
</head>
<body>
<!--
Rest of your code in body
//-->
<!--Body is going to end after the below //-->
<script>
window.addEventListener("DOMContentLoaded",function(){
var sc=document.createElement('script');
sc.src="build/polyfills.js";
document.head.appendChild(sc);
});
</script>
</body>
A webpage is parsed from top to bottom.
So, try put the style in your head tag and place the scripts in just before end of body tag
In the script,
add an eventlistener to window when the Dom content Loaded, make a script tag, append src and append it to Head.
It's important to load the style before anything because it's the visual of your page. It's the first thing the user see.
You change css too along with DOM using javascript. So it is preferred to load css first.
Adding a preload mostly did the trick
<link rel="preload" href="build/main.css" as="style">
The browser now made the request for css. Then it makes a call for js. Then it adds CSS tag to the head. Finally, tries to remakes the call to get CSS but since the call is in progress or done it just loads it. There is an issue of FOUC though if CSS takes times.

Change entire CSS file on refresh

All other questions I've found relating to this are about changing specific elements, or changing the CSS file with a button, but what I'm looking to find out is:
Is there a script that will swap an entire CSS file whenever the page is refreshed?
I.e. I've got my core style.css and supplementary {color}.css files which replace certain elements in style.css, and I'd like those supplementary CSS files to be loaded randomly on refresh.
Sorry, I don't even know where, to begin with this. Hopefully, someone can offer some pointers?
Thank you.
Fundamentally this is just a matter of picking something at random, e.g.:
<head>
<!-- ... -->
<script>
var sheets = ["sheet1.css", "sheet2.css", "sheet3.css"];
var sheet = sheets[Math.floor(Math.random() * sheets.length)];
document.write('<link rel="stylesheet" href="' + sheet + '">');
</script>
<noscript>
<link rel="stylesheet" href="sheet1.css">
</noscript>
<!-- ... -->
(One of the rare cases where document.write isn't actually a bad solution.) Note the noscript fallback will always use the same stylesheet on browsers with JavaScript disabled.
All you need to do to load a CSS-file with Javascript is to add a <link> element to the DOM/body and it will be loaded automatically.
So in your <head> section you could include a <script> tag that just randomly selects a color.css from an array and generate the link tag, preferably as early as possible in the file to prevent flickering.
<script>
var colors = ['red.css', 'blue.css', 'green.css'];
var colors_idx = Math.floor(Math.random()*colors.length);
document.write('<link href="'+colors[ colors_idx ]+'" rel="stylesheet" type="text/css" />');
</script>
(PS. There are cleaner ways to inject HTML, keeping it concise to focus on the solution. Use your favorite approach, document.write can be a bit fickle.)

CSS delivery optimization: How to defer css loading?

I am trying to optimize the CSS delivery following the google documentation for developers https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery#example
As you can see in the example of inlining a small CSS file the critical CSS in inlined in the head and the original small.css is loaded after onload of the page.
<html>
<head>
<style>
.blue{color:blue;}
</style>
</head>
<body>
<div class="blue">
Hello, world!
</div>
</body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>
My question regarding this example:
How to load a large css file after onload of the page?
If you don't mind using jQuery, here is a simple code snippet to help you out. (Otherwise comment and I'll write a pure-js example
function loadStyleSheet(src) {
if (document.createStyleSheet){
document.createStyleSheet(src);
}
else {
$("head").append($("<link rel='stylesheet' href='"+src+" />"));
}
};
Just call this in your $(document).ready() or window.onload function and you're good to go.
For #2, why don't you try it out? Disable Javascript in your browser and see!
By the way, it's amazing how far a simple google search can get you; for the query "post load css", this was the fourth hit...
http://www.vidalquevedo.com/how-to-load-css-stylesheets-dynamically-with-jquery
A little modification to the function provided by Fred to make it more efficient and free of jQuery. I am using this function in production for my websites
// to defer the loading of stylesheets
// just add it right before the </body> tag
// and before any javaScript file inclusion (for performance)
function loadStyleSheet(src){
if (document.createStyleSheet) document.createStyleSheet(src);
else {
var stylesheet = document.createElement('link');
stylesheet.href = src;
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(stylesheet);
}
}
This is how you do it using the new way, without using any 3rd party library or extra JS:
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
link rel="preload" as="style" requests the stylesheet asynchronously.
The onload attribute in the link allows the CSS to be processed when it finishes loading.
"nulling" the onload handler once it is used helps some browsers avoid re-calling the handler upon switching the rel attribute.
The reference to the stylesheet inside of a noscript element works as a fallback for browsers that don't execute JavaScript.
In addition to Fred's answer:
Solution using jQuery & Noscript
<html>
<head>
<style>
.blue{color:blue;}
</style>
<script type="text/javascript" src="../jquery-1.4.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
if($("body").size()>0){
if (document.createStyleSheet){
document.createStyleSheet('style.css');
}
else {
$("head").append($("<link rel='stylesheet'
href='style.css'
type='text/css' media='screen' />"));
}
}
});
</script>
</head>
<body>
<div class="blue">
Hello, world!
</div>
</body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>
from http://www.vidalquevedo.com/how-to-load-css-stylesheets-dynamically-with-jquery
Using pure Javascript & Noscript
<html>
<head>
<style>
.blue{color:blue;}
</style>
<script type="text/javascript">
var stylesheet = document.createElement('link');
stylesheet.href = 'style.css';
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(stylesheet);
</script>
</head>
<body>
<div class="blue">
Hello, world!
</div>
</body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>
Try this snippet
The author claims it was published by Google's PageSpeed Team
<script>
var cb = function() {
var l = document.createElement('link'); l.rel = 'stylesheet';
l.href = 'yourCSSfile.css';
var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h); };
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
</script>
There is a simple way to load CSS asynchronously with only link tag
<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">
media="print" tell browser to load CSS asynchronously that intended for print.
Onload set the link's media to all when it loaded.
Comapared to mahmoud answer, this approach doesn't prioritize fetching the css, which maybe not needed
More details are explained here
WARNING: body{background-image: url("http://example.com/image.jpg");} in css files will make your css files still render-blocking.
If you tried all the solutions above and you still get the render-blocking warning from PageSpeed insights then you probably have this style rule in your css files. After hours of tests it turns out that this rule is what making ALL of my css to be flagged as render-blocking resources at PageSpeed insights. I found the same issue has been discussed before.
I don't know why body{background-image: url(...) do this for all the css files!, although I have different images resources in the file for the buttons, icons, ...etc .
I fixed this by moving this rule from the .css file and put it in the inline styles. Unfortunately, you will have to break your css plan and put the rule in all of your layouts HTML files instead of being in 1 css file that is imported in all of your HTML layouts, but the 90s and green color in PageSpeed insights deserve it.
Fixed mine by introducing placing all css files at the bottom of the page, after the body tag. but this introduces a new problem, the page loads un-styled html for noticeable mili seconds before applying the style. For this I fixed by introducing a splash screen all styled on the page.

Categories