Proper way to reset a radio form

Viewed 64

Here I am trying to run a function "renderQuestion" which will randomize questions and will show the next options once the "next" button is clicked. By "getSelectedValue" I am trying to get the selected radio button.

But what I am fetching is that it shows which button is selected for the first time I press "next" button, but it's not working onward. It's giving some error "Cannot read property 'value' of null".

If I don't use "renderForm.reset()", I can get the values every time I click next. Now, what I am doing wrong here?

const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");

// const q = document.querySelector("#question");
// const ans1 = document.querySelector("#ans1");
// const ans2 = document.querySelector("#ans2");
// const ans3 = document.querySelector("#ans3");
// const ans4 = document.querySelector("#ans4");

renderQuestion = async () => {
  const timeout = async (ms) => new Promise((res) => setTimeout(res, ms));
  let userClicked = false;

  let arr = [ans1, ans2, ans3, ans4]; //array of options
  for (let i = arr.length - 1; i > 0; i--) {    ////randomizing array
    const j = Math.floor(Math.random() * (i + 1));
    const temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }


  // q.innerHTML =
  // `${totalQ}/${ongoing}` + `    ` + qID.data().question;
  // arr[0].innerHTML = qID.data().a;
  // arr[1].innerHTML = qID.data().b;
  // arr[2].innerHTML = qID.data().c;
  // arr[3].innerHTML = qID.data().d;

  next.addEventListener("click", (e) => {
    console.log("inside next");
    let getSelectedValue = document.querySelector('input[name="ans"]:checked');
    console.log(getSelectedValue.value);
    getSelectedValue.checked = false;
    userClicked = true;
    e.preventDefault();
    // renderForm.reset();
  });
  while (userClicked === false) await timeout(50);
};
start = async () =>
{
  for(let i=0;i<4;i++)
  {
    await renderQuestion();
  }
}

start();
#renderForm p {
  display: inline;
}

.option {
  /* display: inline-block; */
  font-size: 1.5rem;
  margin-block: 2rem;
  margin-left: 1rem;
}

.button {
  border: none;
  outline: none;
  font-size: 1rem;
  margin-top: 1rem;
  background-color: #081b0a;
  color: #ffffff;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  font-weight: 100;
  font-family: monospace;
  margin-bottom:8rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="/style.css" />
  <title>Document</title>
</head>

<body>
  <section>
    <form action="" id="renderForm">
      <p id="question"></p>
      <div class="option">
        <input type="radio" name="ans" id="option1" value="ans1" required />
        <p id="ans1">1</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option2" value="ans2" required />
        <p id="ans2">2</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option3" value="ans3" required />
        <p id="ans3">3</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option4" value="ans4" required />
        <p id="ans4">4</p>
      </div>

      <button class="button" id="next" type="submit">Next</button>
    </form>
  </section>

  <script src="/js.js"></script>
</body>

</html>

2 Answers

Move the eventlistener out of the loop. You only need to apply it once, and if you delegate it (meaning apply the listener to a static parent, like document and then test for the target ID), it will work for any #next button you add to the DOM.

document.addEventListener("click", (e) => {
  if (e.target.id !== 'next') return
  console.log("inside next");
  let getSelectedValue = document.querySelector('input[name="ans"]:checked');
  console.log(getSelectedValue.value);
  getSelectedValue.checked = false;
  userClicked = true;
  e.preventDefault();
  // renderForm.reset();
});

const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");

document.addEventListener("click", (e) => {
  if (e.target.id !== 'next') return
  console.log("inside next");
  let getSelectedValue = document.querySelector('input[name="ans"]:checked');
  console.log(getSelectedValue.value);
  getSelectedValue.checked = false;
  userClicked = true;
  e.preventDefault();
  // renderForm.reset();
});

renderQuestion = async() => {
  const timeout = async(ms) => new Promise((res) => setTimeout(res, ms));
  let userClicked = false;

  let arr = [ans1, ans2, ans3, ans4]; //array of options
  for (let i = arr.length - 1; i > 0; i--) { ////randomizing array
    const j = Math.floor(Math.random() * (i + 1));
    const temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }


  while (userClicked === false) await timeout(50);
};
start = async() => {
  for (let i = 0; i < 4; i++) {
    await renderQuestion();
  }
}

start();
#renderForm p {
  display: inline;
}

.option {
  /* display: inline-block; */
  font-size: 1.5rem;
  margin-block: 2rem;
  margin-left: 1rem;
}

.button {
  border: none;
  outline: none;
  font-size: 1rem;
  margin-top: 1rem;
  background-color: #081b0a;
  color: #ffffff;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  font-weight: 100;
  font-family: monospace;
  margin-bottom: 8rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="/style.css" />
  <title>Document</title>
</head>

<body>
  <section>
    <form action="" id="renderForm">
      <p id="question"></p>
      <div class="option">
        <input type="radio" name="ans" id="option1" value="ans1" required />
        <p id="ans1">1</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option2" value="ans2" required />
        <p id="ans2">2</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option3" value="ans3" required />
        <p id="ans3">3</p>
      </div>
      <div class="option">
        <input type="radio" name="ans" id="option4" value="ans4" required />
        <p id="ans4">4</p>
      </div>

      <button class="button" id="next" type="submit">Next</button>
    </form>
  </section>

  <script src="/js.js"></script>
</body>

</html>

At last, I have solved the issue. Initially "renderQuestion" was working every time (4 times here because of for loop) I clicked the "next" button. But whenever I move the eventlistener out of the loop "renderQuestion" function wasn't working on the "next" button click.

It's Because "userClicked" is a local variable and isn't being triggered if I click "next". So, I just changed it to a global variable and everything else same as @kinglish did.

const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");

document.addEventListener("click", (e) => {
  if (e.target.id !== 'next') return
  console.log("inside next");
  let getSelectedValue = document.querySelector('input[name="ans"]:checked');
  console.log(getSelectedValue.value);
  getSelectedValue.checked = false;
  userClicked = true;
  e.preventDefault();
  // renderForm.reset();
});

renderQuestion = async() => {
  console.log("render");
  const timeout = async(ms) => new Promise((res) => setTimeout(res, ms));
  window.userClicked = false;

  let arr = [ans1, ans2, ans3, ans4]; //array of options
  for (let i = arr.length - 1; i > 0; i--) { ////randomizing array
    const j = Math.floor(Math.random() * (i + 1));
    const temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }


  while (userClicked === false) await timeout(50);
};
start = async() => {
  for (let i = 0; i < 4; i++) {
    await renderQuestion();
  }
}

start();
#renderForm  p
{
  display: inline;
}
.option
{
  /* display: inline-block; */
  font-size: 1.5rem;
  margin-block: 2rem;
  margin-left: 1rem;    
}

.button {
    border: none;
    outline: none;
    font-size: 1rem;
    margin-top: 1rem;
    background-color: #081b0a;
    color: #ffffff;
    padding: 0.75rem 1rem;
    border-radius: 0.5rem;
    font-weight: 100;
    font-family: monospace;
    margin-bottom:8rem;
  }
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/style.css" />
    <title>Document</title>
  </head>
  <body>
    <section>
      <form action="" id="renderForm">
        <p id="question"></p>
        <div class="option">
          <input type="radio" name="ans" id="option1" value="ans1" required />
          <p id="ans1">1</p>
        </div>
        <div class="option">
          <input type="radio" name="ans" id="option2" value="ans2" required />
          <p id="ans2">2</p>
        </div>
        <div class="option">
          <input type="radio" name="ans" id="option3" value="ans3" required />
          <p id="ans3">3</p>
        </div>
        <div class="option">
          <input type="radio" name="ans" id="option4" value="ans4" required />
          <p id="ans4">4</p>
        </div>

        <button class="button" id="next" type="submit">Next</button>
      </form>
    </section>

    <script src="/js.js"></script>
  </body>
</html>

Related