1

I would like this code to count up from 0 to 940 (very fast) and alter the text every time it updates

Here's my code (inside my head tag):

    <script type="text/javascript">
      function sleep(milliseconds) {
        const date = Date.now();
        let currentDate = null;
        do {
          currentDate = Date.now();
        } while (currentDate - date < milliseconds);
      }
      function onLoad(){
        var x = document.getElementById("numberID");
        var n = 940;
        var text = "";
        for(i = 0;i < n + 1;i++){
          text = i;
          x.innerHTML = text;
          sleep(1);
        }
      }
    </script>

At the moment, it just waits a second then displays '940' on screen and doesn't display it counting up.

Any help would be appreciated, thanks!

Here's the code I recently put in, still doesn't work:

const x = document.getElementById("numberID");
      function newFrame(duration, start =  performance.now()) {
        requestAnimationFrame((now) => {
          const elapsed = now - start;
          x.innerText = Math.max(0, Math.min(duration, 
          Math.round(elapsed)));
          if(elapsed < duration)
              newFrame(duration, start);
          })
        }
      }
      newFrame(940);
  • Does this answer your question? [JavaScript : For loop with timeout](https://stackoverflow.com/questions/24293376/javascript-for-loop-with-timeout) – Rickard Elimää Jan 04 '20 at 13:41
  • the sleep function is doing nothing. – gugateider Jan 04 '20 at 13:42
  • 1
    `setInterval(function { do_stuff(); }, 1000);`. You can't sleep in JavaScript, but simulate with a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). – Markus Zeller Jan 04 '20 at 14:13

2 Answers2

5

Using a while loop to "sleep" is going to block the page's thread and nothing else can happen in the meantime. This is considered bad practice.

setTimeout guarantees that at least the defined time has passed, but can take (much) longer. This is imprecise, and especially bad for shorter intervals. Same with setInterval. They're also not recommended for callbacks that involve updating the DOM.

What you need to do is use a requestAnimationFrame.

function newFrame(duration, start =  performance.now()) {
  requestAnimationFrame((now) => {
    const elapsed = now - start
    console.log(`time passed: ${elapsed} ms`)
    if(elapsed < duration)
        newFrame(duration, start)
  })
}
newFrame(940)

In your specific case, I'd replace the console.log statement put there for didactic purposes, with something along the lines of:

x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))

Here's what that would look like:

const x = document.getElementById("numberID")

function newFrame(duration, start =  performance.now()) {
  requestAnimationFrame((now) => {
    const elapsed = now - start
    x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))
    if(elapsed < duration)
        newFrame(duration, start)
  })
}

newFrame(940)
<span id="numberID"></span>
Sheraff
  • 5,730
  • 3
  • 28
  • 53
  • Hi, thanks - im just unsure of how to implement this? I'm relatively new to HTML and web development, so I was just wondering if you could help me at all in implementing this piece of code...? Thanks! –  Jan 04 '20 at 14:09
  • @HenryHudson I added a `console.log` statement, try and replace your JS code with mine and look at the DevTools console to see what happens. Then it will just be a matter of replacing my `console.log` with whatever actions you want. Probably a combination of `Math.round()` to get a cleaner number from `elapsed`, and of `.innerText` to put the number in your element. – Sheraff Jan 04 '20 at 14:12
  • does the whole page reload when a new frame happens? –  Jan 04 '20 at 14:16
  • 1
    It's all explained in the link: requestAnimationFrame happens every time the screen updates, and that's the only time you actually need to update anything. It's used in animation to create a smooth animation, otherwise an update can happen in between a repaint of the screen. – Rickard Elimää Jan 04 '20 at 14:18
  • @Sheraff It's more pedagogic to put the code into a snippet for others to see what happens without having to copy-paste it into the devtools. – Rickard Elimää Jan 04 '20 at 14:20
  • 2
    @HenryHudson the browsers try to be smart about this and only update what needs to be updated. But using `requestAnimationFrame` doesn't "make a frame happen", it just tells the browser to call this function *before it was going to draw a frame anyway*, so that you only (and precisely) make calculations when necessary. – Sheraff Jan 04 '20 at 14:20
  • 1
    @RickardElimää that's fair – Sheraff Jan 04 '20 at 14:24
  • @Sheraff check my post - ive edited it with the code I put in from you. –  Jan 04 '20 at 19:53
  • @HenryHudson you should try and re-indent your code properly, it will make it more obvious what is wrong: you have one `}` too many. This error could also have been caught by looking at the DevTools console that will say something like `Unexpected token '}'` and even tell you which line in your code is responsible for the error. – Sheraff Jan 04 '20 at 19:59
  • @HenryHudson at this point, the problem comes from something else. I'd wager that if you haven't changed the structure of your code, you're still declaring `var x` within the scope of your `onload` function, and using it from my `newFrame` function where it doesn't exist. So you'd either have to pass it as an argument, or just put everything in `onload` at the root / global level (outside of the function, just within the ` – Sheraff Jan 04 '20 at 21:24
0

The sleep function is not doing anything, what you need is a setTimeout to display the text at every x milliseconds.

Something like the below will work.

let x = null;
let timeout = null;
const changeText = (text) => {
  x.innerHTML = text;
  clearTimeout(timeout);
}

function onLoad() { 
 x = document.getElementById("numberID");
 const n = 940;
 const t = .01; // in seconds
 for( let i = 0; i <= n; i++) { 
  timeout = setTimeout( () => changeText((i+1).toString()), (t * i) * 1000);  
 }
 
}

onLoad();
<span id="numberID"></span>
gugateider
  • 1,983
  • 2
  • 13
  • 17