skip to Main Content

PROBLEM:
I have a JS script that replaces any occurrences of a certain class (".inserter"), with HTML fetched from another file (e.g., a banner and navigation bar).

Right now, using the "window.onload" event. It seems that it renders the unaltered HTML first, then makes the replacements a split-second later, resulting in an ugly flash.

I want a way to make all the replacements first and then render the final HTML. And to do this it will need access to the DOM.

ATTEMPTS:
I’ve tried using "async", "defer", "DOMContentLoaded" and a few other things that I don’t remember. None of them have worked.

Is there a way to do this with vanilla JS and HTML that doesn’t involve first making the body element invisible with CSS?

Edit:
I was originally avoiding hiding the body with CSS because I wanted the script to be as self contained as possible. But I’ve figured out a way to make it work. (I have answered my own question below).

2

Answers


  1. Chosen as BEST ANSWER

    The easiest solution I have found so far, is to run the script from the head without defer, then in the script, add a style tag to the head that hides the body. After that, use the "window.onload" or similar event to make all the changes first, and then remove the temporary style tag from the head.

    document.head.innerHTML += ('<style id="inserter-screen-flash-prevention"> body {display: none}; </style>');
        
    window.onload = () => { insertAll() };
    
    function insertAll() {
      //...make all replacements...
      document.head.querySelector("#inserter-screen-flash-prevention").remove();
    
    }

    If you are using fetch, make sure to do the removal in the ".then" part of the fetch function.


  2. Yes, there is a way to achieve this using vanilla JavaScript and HTML without making the body element invisible with CSS. You can use a DocumentFragment to create a separate DOM tree, make your changes there, and then append it to the actual DOM.

    Here’s a basic example:

    // Fetch your HTML
    fetch('path/to/your/html')
        .then(response => response.text())
        .then(html => {
            // Create a new DocumentFragment
            let fragment = document.createRange().createContextualFragment(html);
    
            // Make your replacements in the fragment
            fragment.querySelectorAll('.inserter').forEach(el => {
                el.innerHTML = 'Your replacement HTML';
            });
    
            // Append the fragment to the DOM
            document.body.appendChild(fragment);
        });

    In this example, the HTML is fetched from a file, and a DocumentFragment is created from it. The replacements are made within the fragment, which does not cause a reflow of the actual DOM. Once all replacements are made, the fragment is appended to the actual DOM. This should prevent the flash you’re seeing, as the replacements are made before the HTML is rendered on the page.

    Remember to replace path/to/your/html and Your replacement HTML with your actual file path and HTML content.

    This approach should work in most modern browsers. However, please note that fetch and Promise are not supported in Internet Explorer. If you need to support IE, you might need to use a polyfill or use XMLHttpRequest instead of fetch.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search