ARTICLE AD BOX
I'm playing with web components (and lit in particular), and running into the dreaded FOUC problem - the page flashes as it renders. The non-custom-controls are rendered, then replaced with the component content a few milliseconds later.
If I don't use a shadow root (i.e. use the CSS from the document), I can get things to behave if I call customElements.define() from a regular <script> element.
But as soon as I move customElements.define() into a module (which is necessary for libraries like Lit-Element), the flicker comes back. It seems like the explanation here is that modules are forced to load with defer. I can't find any mechanism for using modules and webcomponents that doesn't produce flickering.
Here's code that does not flicker. As soon as I change the script type to "module", the flickering happens.
/js/components/custom-element.js
class CustomInput extends HTMLElement{ constructor(){ super(); } connectedCallback(){ console.log('connectedCallback'); this.innerHTML=` <div class="form-floating mb-1"> <input type="text" placeholder="" class="form-control" id="xxxx" value=""> <label for="xxxx">A Label</label> </div> `; } } customElements.define("custom-input", CustomInput);custom-element.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="head"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" /> <!-- changing type to "module" causes the flicker --> <script src="js/components/custom-element.js" type="text/javascript"></script> </head> <body> <h2 class="text-center">Custom Controls</h2> <div> <custom-input id="ctl1" value="val1"/> </div> <div> <button>A Button</button> </div> <div> <custom-input id="ctl2" value="val2" /> </div> <h2>Content</h2> </body> </html>So - how can I use a webcomponent that is defined in a module without FOUC flickering?
Update: I tried using module preloading, and that made no difference:
<link rel="modulepreload" href="js/components/custom-element.js">Update: I have tried setting body visibility to hidden, then set it back to visible after the custom elements are registered. This is actually working, but it seems like a sledgehammer approach - is this a preferred way to handle this?
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="head"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" /> <style> body{ visibility: hidden; } </style> <script type="module"> import * as abc from '/js/components/custom-element.js'; document.body.style.visibility = 'visible'; </script> </head> <body> <h2 class="text-center">Custom Controls</h2> <div> <custom-input id="ctl1" value="val1"/> </div> <div> <button>A Button</button> </div> <div> <custom-input id="ctl2" value="val2" /> </div> <h2>Content</h2> </body> </html>