3

I wrote the code below and placed it just before the </body> tag on each page of my site. It works great in that it turns the background of each page black and the text to white and remembers that setting until the user changes it back to "Day Mode".

<script>
    var body = $("body");
    var day = $("#day");
    var night = $("#night");
    var dayLi = $("#day-li");
    var nightLi = $("#night-li");
    var panel = $(".panel");
    if(localStorage.night == "on"){
        body.addClass("night");
        panel.addClass("night");
        dayLi.show();
        nightLi.hide();
    }else{
        body.removeClass("night");
        panel.removeClass("night");
        nightLi.show();
        dayLi.hide();
    }
    night.click(function(){
        body.addClass("night");
        panel.addClass("night");
        localStorage.setItem("night","on");
        dayLi.show();
        nightLi.hide();
    });
    day.click(function(){
        body.removeClass("night");
        panel.removeClass("night");
        localStorage.night = "off";
        nightLi.show();
        dayLi.hide();
    });
</script>

The .night class is as you might expect:

.night{
        background-color: black;
        color: white;
    }

The problem I'm having with it is that when in night mode, it takes about a second for night mode to take effect, meaning there is a white flash on each page load.

I experimented with placing the above script higher up on the page, but still got the flash of white first on each page load. To be clear, the flash of white is very short or almost non-existent on pages that aren't loading as much data from my database. But on the pages where a lot of data is getting loaded from my db the flash lasts for about a second.

Is this something that can be fixed? Thanks in advance for any pointers.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Jake 1986
  • 582
  • 1
  • 6
  • 25
  • 2
    You have a FOUC'ing problem (FOUC = [Flash Of Unstyled Content](https://en.wikipedia.org/wiki/Flash_of_unstyled_content)) – adeneo Nov 02 '16 at 20:18
  • 1
    The style should be rendered as css from the server. That will prevent the flashing. – Travis J Nov 02 '16 at 20:18
  • 2
    You can switch the initial mode on the server side. There won't be any glitches if you do that. – Taha Paksu Nov 02 '16 at 20:19
  • @adeneo haha thanks - I was not aware of this acronym. – Jake 1986 Nov 02 '16 at 20:21
  • @TahaPaksu thanks! I will move this function to the server – Jake 1986 Nov 02 '16 at 20:23
  • 1
    @TravisJ thank you, I will move this over to the server. – Jake 1986 Nov 02 '16 at 20:23
  • 1
    Unfortunately, it is impossible to completely eliminate the flash of white because no content defaults to white, so your page will always show white for a few frames. However, reducing the amount of time that the page shows as white is possible. – tcooc Nov 02 '16 at 20:24
  • @tcooc I see, hopefully moving this to the server will reduce the duration of the FOUC – Jake 1986 Nov 02 '16 at 20:26
  • 2
    Also contrary to other posts/comments, using CSS is as effective as using javascript (which is just as effective as rendering the "night mode" server-side). The trick is not what you do, but where you do it in the page's load. – tcooc Nov 02 '16 at 20:26
  • 1
    @tcooc - That's not neccessarely true, CSS loaded in the head would already work on elements as they load, while javascript would have to wait for the elements to actually finish loading before being able to access them, and even then, you'd need a script tag after every single element to change that element before the next one loads etc. Swapping stylesheets on the serverside would also have the benefit of not loading unneccessary files. – adeneo Nov 02 '16 at 20:44
  • This isn't a 100% fix for your issue, however I recently asked a question that is along the same lines.... Where I had a loading gif playing whilst all my content was being loaded, and then on complete, would toggle on the content & off the gif. The answer I got might give you some food for thought. http://stackoverflow.com/questions/37314240/recall-window-load-event-javascript – Zze Nov 02 '16 at 20:52
  • @adeneo I was only taking into account how fast the css change would apply to the page, and ignoring the other trade-offs you mentioned. I agree that the stylesheet approach is better for multiple elements, but drawbacks are that the styles must be inline (since `` stylesheets are loaded asynchronously), and rendered server-side, while scripts can be offloaded into other files and do the change client-side. – tcooc Nov 02 '16 at 20:56

2 Answers2

3

As I've mentioned, it's impossible to fully eliminate the flash, but you can reduce it greatly by doing it in the head and not using classes (since stylesheets are loaded asynchronously).

Add a script right after your <body> opening tag that does this logic:

<script>
if(localStorage.night == "on") {
  document.body.style.backgroundColor = "#000";  // color of your choice
}
</script>

Note that you can't use jquery because it isn't loaded at that point. This will minimize the time that your page is the wrong color.

Edit: Another possible solution is to do changes server-side using CSS, which should have the same effect. The catch is that you need to render the styles inline since CSS files are loaded asynchronously.

tcooc
  • 20,629
  • 3
  • 39
  • 57
2

To prevent a Flash of Unstyled Content (FOUC) on the frontend alone you can use a "cloak" trick. AngularJS and VueJS both use this technique for single page applications.

In your HTML, set a style tag to hide your <body>.

<style>
    body {
        display: none;
    }
</style>

Then, in your existing code, or even a standalone script, you can do:

$(function() {
    $(body).css('display', 'initial');
});

This works because the embedded styles will run first, hiding your body element. Then, once the javascript is loaded and the DOM is ready, the script will run, unhiding the body.

You could enhance this to always start as black by updating the embedded styles to:

body * {
    display: none;
    background: #000;
}

This will make the body black and hide all its descendants.

Soviut
  • 88,194
  • 49
  • 192
  • 260
  • Not sure who down voted this. Tis' a great solution! – Zze Nov 02 '16 at 20:55
  • 1
    I don't get how the display thing works alone, since default background in most browser is white. Imo it actually can't work without having the embedded style, which doesn't solve OP problems. – Gregoire D. Nov 19 '16 at 17:43