Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DOM Background color propagation flickering inheriting initial bg color in Google Chrome using theme toggle to overload body bg color

I'm currently working on a theme toggle for my website that uses Javascript / jQuery to manipulate the Body.bg color using a lightmode()/ darkmode() function that is toggled by a button. What I want to do is create seamless transitioning between the body bg color with fade in and fade outs. I already have that made and created, but the problem is when the theme reads the storage type it will blink quickly in Chrome and Chrome Canary but in Safari, and Safari Tech Preview for Catalina it works seamlessly.

However, I keep running into an issue when the user switches to white and then clicks on the nav link which then causes a black blink of the dark mode theme. My site starts with dark mode enabled and body bg is = #0a0a0a, but when it switches to white and the storage is updated, it still inherits the black body - bg that is initially set in the style.less of my theme.

If I remove the background color, then the white flicker happens on the dark mode theme, and ideally my end goal is to create seamless page navigation without the page blinking for a quick second reading the initial bg color before the body loads so - white on white bg, black on black bg no blinking to cause visual disruption.

I've tried every work around I could come up with and can't figure out a solution. It works in Safari with no page blinking, but in Chrome it still blinks. I've included video links showing what I want to accomplish with the Safari render for Google chrome and the main.js code file.

Safari seamless transitioning with no blink

Safari - seamless transitioning

Chrome Canary

Chrome and Chrome Canary - watch for the blink on white transition. It's quick but super frustrating.

So what's going on?! The problem is the theme is now set to white, but the initial body - bg color is black in the style.less theme and it picks that up real quick before snapping back to the white theme.

When I audit the site in Canary, and run it at slow 3G simulated there is no blink, but when it ran normally or at Fast 3G in the audit the blink occurs. My hypothesis is to set a timeout on the body before loading it, but I tried that as a work around and still got blinking.

I've tried to create key stored in local storage for my storage and to also pause the body from loading, but so far I can't find a solution to this :/

So what I want to do is to stop the body bg color from flickering white or black based off the theme colors base color without blinking.

Thanks for taking the time to read my problem!

document.addEventListener("DOMContentLoaded", function() {
document.body.style.backgroundColor = "inherit";

if (document.readyState === 'complete') {

if(lightmodeON == true) {
  $('body').css({background: "#FFF"});
  console.log('loading white bg');
}

if(lightmodeON == false) {
  $('body').css({background: "#0a0a0a"});
  console.log('loading black bg');
}
}


if (typeof (Storage) !=="undefined") {
if (localStorage.themepref == 1 ) {
  lightmode();
}
else {
  darkmode();
  localStorage.themepref = 2;
}

if(lightmodeON == true) {
  $('body').css({background: "#FFF"});
  console.log('loading fffwhite bg');
}

if(lightmodeON == false) {
  $('body').css({background: "#0a0a0a"});
  console.log('loading black bg');
}
}

Here is my version of trying to make a work around.

var clickDelay = false;
var themeType = -1;
var lightmodeON = false;


window.onload = function() {
  console.log('First');

  if (event.target.readyState === 'interactive') {

      $('body').css({ background: ''});
      document.body.style.backgroundColor = "inherit";

   if(lightmodeON == true) {
     $('body').css({background: "#FFF"});
       document.body.style.backgroundColor = "#FFF";
   }

   if(lightmodeON == false) {
     $('body').css({background: "#0a0a0a"});
       document.body.style.backgroundColor = "#0a0a0a";
   }
}
    overloadBG();
};


document.addEventListener("DOMContentLoaded", function() {
  document.body.style.backgroundColor = "inherit";

  if (document.readyState === 'complete') {

    if(lightmodeON == true) {
      $('body').css({background: "#FFF"});
      console.log('loading white bg');
    }

    if(lightmodeON == false) {
      $('body').css({background: "#0a0a0a"});
      console.log('loading black bg');
    }
  }


  if (typeof (Storage) !=="undefined") {
    if (localStorage.themepref == 1 ) {
      lightmode();
    }
    else {
      darkmode();
      localStorage.themepref = 2;
    }

    if(lightmodeON == true) {
      $('body').css({background: "#FFF"});
      console.log('loading fffwhite bg');
    }

    if(lightmodeON == false) {
      $('body').css({background: "#0a0a0a"});
      console.log('loading black bg');
    }
  }



});

window.addEventListener('beforeunload', function (e) {
  $('body').css({ background: ''});

  if(lightmodeON == true) {
    $('body').css({background: "#FFF"});
      document.body.style.backgroundColor = "#FFF";
  }

  if(lightmodeON == false) {
    $('body').css({background: "#0a0a0a"});
      document.body.style.backgroundColor = "#0a0a0a";
  }

  document.body.style.backgroundColor = "inherit";
  overloadBG();
  
});

window.onbeforeunload = function () {
  //FUCK YOU BLINK
  //$('body').css({ background: 'none'});
  $('body').css({ background: ''});
    document.body.style.backgroundColor = "";

  if (typeof (Storage) !=="undefined") {
    if (localStorage.themepref == 1 ) {
        localStorage.themepref = 1;
        lightmode();
    }
    else {
      darkmode();
      localStorage.themepref = 2;
    }
  }

}

document.onreadystatechange = function() {


    $('body').css({ background: ''});
    document.body.style.backgroundColor = "transparent";

    if (event.target.readyState === 'interactive') {
        console.log('interactive');

             if(lightmodeON === true) {
               $('body').css({background: "#FFF"});
             }

             if(lightmodeON === false) {
               $('body').css({background: "#0a0a0a"});
             }
      }

      if (event.target.readyState === 'loading') {

          $('body').css({ background: ''});

       if(lightmodeON == true) {
         $('body').css({background: "#FFF"});
         $("body").css("cssText", "background: #FFF !important;");
       }

       if(lightmodeON == false) {
         $('body').css({background: "#0a0a0a"});
         $("body").css("cssText", "background: #0a0a0a !important;");
       }
    }
like image 926
Monstr92 Avatar asked Aug 27 '19 00:08

Monstr92


1 Answers

By the time DOMContentLoaded will fire, the browser may already have painted your page's background.

Here is a demo which does reload the page indefinitely, be sure to click "hide results" when you're done watching it, it may make your computer use some CPU resources (but no network ones).

/* 
  We use an iframe to avoid making real network requests 
  Below is the HTML content of this iframe
*/

const content = `<!DOCTYPE html>
<html>
  <head>
    <style>
      /* default bg color */
      body { background: red; }
    </style>
    <script>
      document.addEventListener('DOMContentLoaded', e => {
        document.body.style.backgroundColor = 'green';
        location.reload() // infinite reload...
      });
    <\/script>
  </head>
  <body>
    <div>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi blandit cursus orci ut maximus. Phasellus pharetra lobortis pellentesque. Pellentesque et lacus et lorem facilisis ultrices. Maecenas sed ultricies nisi. Suspendisse feugiat finibus justo, id consectetur turpis aliquam ac. Proin sit amet laoreet velit. Nullam felis lectus, commodo imperdiet mollis in, ullamcorper eget tortor. Nullam at metus non diam faucibus aliquam. Vestibulum eu maximus risus, vitae elementum justo. Fusce commodo lacus a augue lobortis, quis ornare odio gravida. Quisque ultrices tempus tellus, vitae pulvinar est rutrum in. Duis ante erat, placerat sit amet imperdiet vitae, facilisis non mauris. Integer eu ex sapien.</p>
      <p>Morbi porttitor justo eu sodales efficitur. Integer ut suscipit libero, sed dapibus velit. Vestibulum laoreet neque ac odio consequat, a suscipit arcu tristique. Curabitur tempor, nisl eu porttitor feugiat, nibh lorem laoreet massa, ut porta tellus augue accumsan metus. Suspendisse sed venenatis neque. Aliquam non justo in tortor dictum suscipit. Duis eu lectus eu dui placerat luctus. Etiam et volutpat diam, nec ullamcorper tellus. Nullam nibh dui, bibendum a ipsum et, elementum tempor mi. Maecenas ut eros eu sem malesuada tincidunt. Aenean fermentum sit amet augue quis vulputate. Vivamus commodo pellentesque purus rhoncus suscipit. Proin et enim vel ipsum vulputate mollis venenatis ut enim. Curabitur eget velit mollis, luctus sem at, aliquam est. Donec quis elit erat. Nullam facilisis lorem nisl, a luctus purus tristique vel.</p>
      <p>Donec in magna at ante mollis sodales ac vitae mauris. Aliquam condimentum ligula nulla, scelerisque cursus neque consequat quis. Fusce vestibulum nisi vitae ipsum venenatis, a pharetra diam tempus. Aenean maximus enim orci, quis mollis neque sollicitudin et. Quisque viverra ipsum vitae magna varius, id ornare justo dictum. Quisque eleifend magna ac congue dignissim. Duis eu volutpat quam, quis placerat tellus. Pellentesque felis mi, imperdiet eu semper vel, hendrerit sit amet ex.</p>
    </div>
  </body>
</html>
`;
frame.src = URL.createObjectURL(new Blob([content], {type: 'text/html'}));
<iframe id="frame" widht="500" height="500"></iframe>

To workaround this issue, move your scripts at the top of the <body> and set the initial <body> color from Storage as soon as possible.
You will still be able to wait for the Document is ready before attaching other events.

/* 
  We use an iframe to avoid making real network requests 
  Below is the HTML content of this iframe
*/

const content = `<!DOCTYPE html>
<html>
  <head>
    <style>
      /* default bg color */
      body { background: red; }
    </style>
  </head>
  <body>
    <!-- move inside <body> -->
    <script>
      // first retrieve from Storage
      // const lightModeOn = locaStorage.getItem('lightmode') === "1";
      // StackSnippets don't allow Storage...
      const lightModeOn = true;
      // set directly the body's style, we're in so we don't need to wait, it's already available
      document.body.style.backgroundColor = 'green';
      // then wait for DOMContentLoaded if you wish to add listeners to other events
      document.addEventListener('DOMContentLoaded', e => {
      // $('.toggle').on('input', switchLight);

      // just to demonstrate it works
      location.reload();
   });
      
    <\/script>

    <div>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi blandit cursus orci ut maximus. Phasellus pharetra lobortis pellentesque. Pellentesque et lacus et lorem facilisis ultrices. Maecenas sed ultricies nisi. Suspendisse feugiat finibus justo, id consectetur turpis aliquam ac. Proin sit amet laoreet velit. Nullam felis lectus, commodo imperdiet mollis in, ullamcorper eget tortor. Nullam at metus non diam faucibus aliquam. Vestibulum eu maximus risus, vitae elementum justo. Fusce commodo lacus a augue lobortis, quis ornare odio gravida. Quisque ultrices tempus tellus, vitae pulvinar est rutrum in. Duis ante erat, placerat sit amet imperdiet vitae, facilisis non mauris. Integer eu ex sapien.</p>
      <p>Morbi porttitor justo eu sodales efficitur. Integer ut suscipit libero, sed dapibus velit. Vestibulum laoreet neque ac odio consequat, a suscipit arcu tristique. Curabitur tempor, nisl eu porttitor feugiat, nibh lorem laoreet massa, ut porta tellus augue accumsan metus. Suspendisse sed venenatis neque. Aliquam non justo in tortor dictum suscipit. Duis eu lectus eu dui placerat luctus. Etiam et volutpat diam, nec ullamcorper tellus. Nullam nibh dui, bibendum a ipsum et, elementum tempor mi. Maecenas ut eros eu sem malesuada tincidunt. Aenean fermentum sit amet augue quis vulputate. Vivamus commodo pellentesque purus rhoncus suscipit. Proin et enim vel ipsum vulputate mollis venenatis ut enim. Curabitur eget velit mollis, luctus sem at, aliquam est. Donec quis elit erat. Nullam facilisis lorem nisl, a luctus purus tristique vel.</p>
      <p>Donec in magna at ante mollis sodales ac vitae mauris. Aliquam condimentum ligula nulla, scelerisque cursus neque consequat quis. Fusce vestibulum nisi vitae ipsum venenatis, a pharetra diam tempus. Aenean maximus enim orci, quis mollis neque sollicitudin et. Quisque viverra ipsum vitae magna varius, id ornare justo dictum. Quisque eleifend magna ac congue dignissim. Duis eu volutpat quam, quis placerat tellus. Pellentesque felis mi, imperdiet eu semper vel, hendrerit sit amet ex.</p>
    </div>
  </body>
</html>
`;
frame.src = URL.createObjectURL(new Blob([content], {type: 'text/html'}));
<iframe id="frame" widht="500" height="500"></iframe>
like image 93
Kaiido Avatar answered Sep 19 '22 06:09

Kaiido