skip to Main Content

So, I am trying to make a random number generator with HTML and JavaScript. I want it to have a thing where it goes up by one until it reaches the random number so it has an "animation" of some sort, but I can’t make it work. Here is my code:

document.getElementById("numbergenerator").onclick = generateNumber;

function generateNumber() {
    numbervalue = Math.floor(Math.random() * 1001);
  for(var i = 0; i < numbervalue; i++) {
    setTimeout(goUpThing(i), 10);
  }
  document.getElementById("numberdisplay").innerHTML = numbervalue;
}

function goUpThing(number) {
    document.getElementById("numberdisplay").innerHTML = number;
}
<html>
  <body>
    <h1>
      <em>random stuff</em>
    </h1>
    <hr>
    <h2 id="numberdisplay">
      there is no number yet
    </h2>
    <div>
    <button id="numbergenerator" type="button">
      generate a random number
    </button>
    </div>
  </body>
</html>

Can someone please help me? By the way I am using JSFiddle to run my code.

2

Answers


  1. You can use setInterval() and clearInterval().

    The setInterval allows a function to run at intervals set in ms ( 1 in this example).
    It is assigned to a variable named interval for later reference.
    The inner function increments the counter on each interval until the condition is met – the counter is equal to random numbervalue. Then the previous variable interval is passed to clearInterval and it stops running the inner function

    document.getElementById("numbergenerator").onclick = generateNumber;
    
    function generateNumber() {
      let numbervalue = Math.floor(Math.random() * 1001);
      let counter = 0;
      let interval = setInterval(() => {
        document.getElementById("numberdisplay").innerHTML = counter;
        if (counter === numbervalue) {
          clearInterval(interval);
        }
        counter++;
      }, 1);
    }
      <body>
        <h1>
          <em>random stuff</em>
        </h1>
        <hr>
        <h2 id="numberdisplay">
          there is no number yet
        </h2>
        <div>
        <button id="numbergenerator" type="button">
          generate a random number
        </button>
        </div>
      </body>
    Login or Signup to reply.
  2. There are several ways to achieve this.

    • You can use async/await in combination with setTimeout or requestAnimationFrame to wait for the next number to be displayed. (example 1 & 2)
    • You can start multiple timeouts at once, each delayed depending on the number generated. (example 3)
    • You can use an interval with an end condition. (example 4)
    • You can use requestAnimationFrame()-loop with an end condition. (example 5)
    • You can generate a promise-chain with setTimeout or requestAnimationFrame to wait for the next number to be displayed. (example 6)

    And possibly some more.

    All examples are displaying a random number from 1-1000.

    The execution time of the example may vary. requestAnimationFrame() should be used if every number must be displayed at least once. It may be "slower" than an interval or timeout with 10ms as it depends basically on the refresh rate of the screen. On 60hz it will be similar to an interval with 17ms (1000 / 60 = 16.666~), while the setTimeout or setInterval callbacks may be called at the same frame multiple times.

    Example 3-6 will work also in older browsers without async/await support, you may also need to change the arrow functions to classic functions if required. Example 6 may also require a Promise polyfill if Promise isn’t supported.

    Example 1

    Generate a random number from 1 to 1000, inside the loop wait for the timeout to end before the script continues.

    // example 1 using async/await w/ setTimeout on a promise callback
    button.onclick = async () => {
      button.disabled = true;
      let rand = Math.ceil(Math.random() * 1000);
      for (let i = 1; i <= rand; i++) {
        output.innerText = i;
        /**
         * You could also define a sleep function and await it
         *   function sleep(t) { return new Promise(r => setTimeout(r,t); }
         *
         *   await sleep(10);
         */
        await new Promise(resolve => setTimeout(resolve, 10));
      }
      button.disabled = false;
    }
    <h4>Example 1: async function w/ await setTimeout</h4>
    <p id="output">click the button</p>
    <button id="button" type="button">launch</button>

    Example 2

    Similar to #1, it just waits for the next frame.

    // example 2 using async/await w/ requestAnimationFrame on a promise
    // this example will decrease the speed hardly,
    // if you switch to another tab while it's counting up
    button.onclick = async () => {
      button.disabled = true;
      let rand = Math.ceil(Math.random() * 1000);
      for (let i = 1; i <= rand; i++) {
        output.innerText = i;
        await new Promise(resolve => requestAnimationFrame(resolve));
      }
      button.disabled = false;
    }
    <h4>Example 2: async function w/ await requestAnimationFrame</h4>
    <p id="output">click the button</p>
    <button id="button" type="button">launch</button>

    Example 3

    Start multiple timeouts at once, each delayed by 10 * numberToDisplayms.

    // example 3 starting N setTimeout calls at once
    button.onclick = () => {
      button.disabled = true;
      let rand = Math.ceil(Math.random() * 1000);
      for (let i = 1; i <= rand; i++) {
        setTimeout((value) => {
          output.innerText = value;
          // pass current value of i to the function in the setTimeout call
        }, i * 10, i);
      }
      setTimeout(() => {
        button.disabled = false;
      }, 10 * rand + 10)
    }
    <h4>Example 3: starting N setTimeout requests at the same time</h4>
    <p id="output">click the button</p>
    <button id="button" type="button">launch</button>

    Example 4

    Using setInterval with an end condition.

    // example 4 setInterval with end condition
    button.onclick = () => {
      button.disabled = true;
      const rand = Math.ceil(Math.random() * 1000);
      const interval = setInterval((obj) => {
        output.innerText = ++obj.v;
        if (obj.v == rand) {
           clearInterval(interval);
           button.disabled = false;
        }
        // passing an object, to keep the reference for increment,
        // you could also define the counter outside the function 
        // using let or var and increase it's value on every tick
      }, 10, { v: 0 })
    }
    <h4>Example 4: setInterval with end condition</h4>
    <p id="output">click the button</p>
    <button id="button" type="button">launch</button>

    Example 5

    Similar to #4, but this one uses requestAnimationFrame with an end condition and increases the passed value each time the function is called starting from 0 with the first number displayed is 1.

    // example 5: requestAnimationFrame-loop with end condition
    // like example 2, this will decrease the speed hardly,
    // if you switch to another tab while it's running
    button.onclick = () => {
      button.disabled = true;
      const rand = Math.ceil(Math.random() * 1000);
      (function count(i) {
        output.innerText = ++i;
        if (i == rand) {
          button.disabled = false;
        } else {      
          requestAnimationFrame(() => count(i));
        }
      })(0) // invoke the function directly to start
    }
    <h4>Example 5: requestAnimationFrame-loop with end condition</h4>
    <p id="output">click the button</p>
    <button id="button" type="button">launch</button>

    Example 6

    This works similar to #1 and #2 and makes sure that the next number is only displayed, after the promise is fulfilled.

    // example 6: Promise-Chain
    button.onclick = () => {
      button.disabled = true;
      let rand = Math.ceil(Math.random() * 1000);
      let prom = Promise.resolve(0);
      while (rand--) {
        prom = prom.then(v => {
          output.innerText = ++v;
          return new Promise(res => setTimeout(res, 10, v))
        });
      }
      prom.finally(() => {
        button.disabled = false;
      });
    }
    
    buttonRAF.onclick = () => {
      buttonRAF.disabled = true;
      let rand = Math.ceil(Math.random() * 1000);
      let prom = Promise.resolve(0);
      while (rand--) {
        prom = prom.then(v => {
          outputRAF.innerText = ++v;
          return new Promise(res => requestAnimationFrame(() => res(v)))
        });
      }
      prom.finally(() => {
        buttonRAF.disabled = false;
      });
    }
    <h4>
      Example 6: Promise-Chain
      <button onclick="button.click(), buttonRAF.click()">both</button>
    </h4>
    <p id="output">click the button</p>
    <button id="button">launch (setTimeout)</button>
    
    <p id="outputRAF">click the button</p>
    <button id="buttonRAF">launch (requestAnimationFrame)</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search