skip to Main Content

I have the following DOM element change function using vanilla JavaScript where I change the span element that contains some text string of the DOM with a page load event.

With the following code, the DOM elements are changed as expected. However, they still see a minimal fraction of flickering before the DOM element change for the variable desktop and mobile.

Example of the flickering scenario:

  • I see the tag span "Text I want to change" for a fraction of second.
  • I see the changed tag span "text1 changed" after the content has fully loaded.

I think this is happening because the DOM changes are applied when the DOM content of the page is fully loaded. I would like to hide the existing tag elements until the changes have been applied and then display them.

The elements structure for desktop and mobile I want to manipulate is like the following:

<span class="first-headline target-span">Text I want to change</span>

See the concept below:

var changesObj = {
  "us": {
    title: "text1 changed"
  },
  "eu": {
    title: "text2 changed"
  }
};

function changes() {
  var webshop = changesObj[window.location.pathname.split('/')[1]];
  console.log(webshop);
  var changesText;
  if (!webshop) {
    console.log('webshop not found');
  }
  changesText = webshop.title;
  
  if (document.querySelector('.target-span').innerText.length > 0) {
    var desktop = document.querySelector('.target-span');
    console.log(desktop);
    desktop.innerText = changesText;
    console.log(desktop.innerText);
    console.log("applied changes for dekstop");
  }
  if (document.querySelector('.target-span').innerText.lenght > 0) {
    var mobile = document.querySelector('.target-span');
    mobile.innerText = changesText;
    console.log(mobile.innerText);
    console.log("applied changes for mobile");
  }
}

function invokeChanges() {
  document.addEventListener("DOMContentLoaded", function () {
    changes();
  });
}

invokeChanges();

Is there a way to initially hide the DOM element until the change to the existing element has been applied and then show it?

I was thinking of using something inline CSS rules like the following:

Set .style.visbility='hidden' and when the text content is changed to show it with .style.visbility='visble'.

But I’m not sure how this solution should be appropriately implemented in my code.

3

Answers


  1. Quick & Dirty:

    Place the script tag just below your span tag:

    <span class="first-headline target-span">Text I want to change</span>
    
      <script>
        var changesObj = {
          "us": {
            title: "text1 changed"
          },
          "eu": {
            title: "text2 changed"
          }
        };
    
        function changes() {
          var webshop = changesObj[window.location.pathname.split('/')[1]];
          console.log(webshop);
          var changesText;
          if (!webshop) {
            console.log('webshop not found');
          }
          changesText = webshop.title;
    
          if (document.querySelector('.target-span').innerText.length > 0) {
            var desktop = document.querySelector('.target-span');
            console.log(desktop);
            desktop.innerText = changesText;
            console.log(desktop.innerText);
            console.log("applied changes for dekstop");
          }
          if (document.querySelector('.target-span').innerText.lenght > 0) {
            var mobile = document.querySelector('.target-span');
            mobile.innerText = changesText;
            console.log(mobile.innerText);
            console.log("applied changes for mobile");
          }
        }
    
        changes();
      </script>
    

    This way you avoid the asynchronous code part.

    Login or Signup to reply.
  2. There are a few reasons as to why it’s flickering, but two preventative steps you can take:

    • Use defer on your script tag <script defer>, as this lets the browser handle the order your scripts are run instead of DOMContentLoaded. You also get to avoid the changes wrapper function.
    • As suggested by this question (and your own reasoning) attach inline CSS / a CSS file to the page to set the text as invisible, then mark it as visible
    • If it doesn’t affect the layout of your page too much, you can also opt to dynamically generate the element instead.

    However, do note that regardless of any of these, these still require JS to be executed and may still have a delay. If you are able to use it, you may be interested in prerendering your webpage – from your JS code, it looks for the route name if its eu or us, which means your page is pre-renderable.

    Login or Signup to reply.
  3. In any case you need to wait until the DOM has been loaded before you can manipulate it. Using an even listener for DOMContentLoaded would be the way to go. So, three things need to happen:

    1. Wait for the DOM to load
    2. Find the elements and change the text
    3. Make the elements visible. You can either use the property visibility: hidden or display: none. Difference is that with visibility: hidden the element will still take up space. So, the choice depends on the context.

    In the first example I add a timeout so that you can see what the page looks like just before the text is changed. I also styled the <p> (display: inline-block) so that you can see the size of the hidden span.

    window.location.hash = "us"; // for testing on SO
    var changesObj = {
      "us": { title: "text1 changed" },
      "eu": { title: "text2 changed" }
    };
    
    function changes(e) {
      //let webshop = changesObj[window.location.pathname.split('/')[1]];
      let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
      if (webshop) {
        [...document.querySelectorAll('.target-span')].forEach(span => {
          span.textContent = webshop.title;
          span.classList.add('show');
        });
      }
    }
    
    document.addEventListener('DOMContentLoaded', e => {
      setTimeout(changes, 1000);
    });
    p {
      border: thin solid black;
      display: inline-block;
    }
    
    .target-span {
      visibility: hidden;
    }
    
    .target-span.show {
      visibility: visible;
    }
    <p>
      <span class="first-headline target-span">Text I want to change</span>
    </p>
    <p>
      <span class="first-headline target-span">Text I want to change</span>
    </p>

    In the second example I combined all the code into one HTML page. Notice that the style is defined in the header. So, when the DOM is passed the CSS will be passed as well without having to load a external style-sheet (well, there are tricks for that as well, but out of scope for this answer).

    <html>
    
    <head>
      <style>
        p {
          border: thin solid black;
        }
        .target-span {
          visibility: hidden;
        }
        .target-span.show {
          visibility: visible;
        }
      </style>
      <script>
        window.location.hash = "us"; // for testing on SO
        var changesObj = {
          "us": {title: "text1 changed"},
          "eu": {title: "text2 changed"}
        };
        
        function changes(e) {
          //let webshop = changesObj[window.location.pathname.split('/')[1]];
          let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
          if (webshop) {
            [...document.querySelectorAll('.target-span')].forEach(span => {
              span.textContent = webshop.title;
              span.classList.add('show');
            });
          }
        }
        document.addEventListener('DOMContentLoaded', changes);
      </script>
    </head>
    
    <body>
      <p>
        <span class="first-headline target-span">Text I want to change</span>
      </p>
      <p>
        <span class="first-headline target-span">Text I want to change</span>
      </p>
    </body>
    
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search