How to disable a button until all the input fields are filled using only JavaScript?

834 Views Asked by At

I am creating a quiz game, but I only want the user to be able to submit if all of the input fields are filled out using only JavaScript. I saw other similar questions on StackOverflow that used jquery, but I want to only use JavaScript.

html (Django template)


    <h1 class="all_headings" id="quiz_title">{{ quiz.title }}</h1>
        <h4 class="quiz_description">By: {{ quiz.user }}</h4>
        <h4 class="quiz_description">Created On: {{ quiz.date_and_time }}</h4>
        <br>
    
        {% for q in quiz_questions %}
            <h3 class="quiz_questions">{{q.1}}
                <input type="text" id="id_question{{q.0}}" class="input">
            </h3>
            <div id="div{{ q.0 }}"></div>
            <br>
            <br>
        {% endfor %}
        <input type="submit" value="Submit" class="button quiz_button" id="{{ quiz.id }}"> 
     
        <h2 id="score"></h2>
        <input type="hidden" id="user_id" value="{{ request.user.id }}">
        <input type="hidden" id="user_points" value="{{ request.user.points }}">
        {% if request.user.is_anonymous %}
            <h3 class="all_headings"><a href="{% url 'register' %}">Join now</a> to earn 10 points for every question you answer correctly!</h3>
        {% endif %}

Update: I added the entire Django template code so that you can see what is going on.

current JavaScript

function bindText(e){
    const required = [...document.querySelectorAll(".inputs")];
    required.forEach(input => input.oninput = checkText);
}

function checkText(e){
    const required = [...document.querySelectorAll(".inputs")];
    const button = document.getElementsByClassName(".quiz_button");
    button.disabled = !required.every(input => input.value.length > 0);
}



document.addEventListener('DOMContentLoaded', function(){
    //gets all the quiz_buttons                                                                                                                                        enableChecking();
    const quizButton = document.querySelectorAll('.quiz_button');
    for (const button of quizButton){
        button.addEventListener('click', (event) => check_quiz(event.target.id));
    }
})

function check_quiz(id){
    console.log("button is clicked");

    //get answers
    let response1 = document.getElementById('id_question1').value.toUpperCase().replace(/\s/g, "");
    let response2 = document.getElementById('id_question2').value.toUpperCase().replace(/\s/g, "");
    //repeats 8 more times

    //get divs
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    //repeats 8 more times

    var correctAnswers = 0;

    //get quiz
    fetch(`/check/${id}`)
    .then(response => response.json())
    .then(quiz => {
        rightM = "You got this correct. Great job!";
        //checking if the answers are right
        //#1
        let answ1 = quiz.answer1.toUpperCase().replace(/\s/g, "");
        if(answ1 === response1){
            div1.innerHTML = rightM;
            div1.classList.add("correct"); 
            correctAnswers++;
        } else{
            div1.innerHTML = `The correct answer is ${quiz.answer1}. Nice try!`;
            div1.classList.add("incorrect");
        }
        //#2
        let answ2 = quiz.answer1.toUpperCase().replace(/\s/g, "");
        if(answ2 === response2){
            div2.innerHTML = rightM;
            div2.classList.add("correct");
            correctAnswers++;
        } else{
            div2.innerHTML = `The correct answer is ${quiz.answer2}. Nice try!`;
            div2.classList.add("incorrect");
        }
        //repeats 8 more times

        console.log(correctAnswers)
        //display score
        let score = document.getElementById("score");
        score.innerHTML = `Your score is ${correctAnswers}. Great job! :)`;
        score.classList.add("score_style");

         //points
        let newPoints = correctAnswers * 10;
        let currentUser = parseInt(document.getElementById("user_id").value);
        let currentPoints = parseInt(document.getElementById("user_points").value);

        let numOfPoints = currentPoints + newPoints;
        console.log(numOfPoints);

        fetch(`/points/${currentUser}`,{
            method: "PUT", 
            body: JSON.stringify({
                points: numOfPoints
            })
        })
    })
}

I updated this with my current code and fixed all the errors from Update 2 of the answer, but the submit button still does not work when it is pressed.

1

There are 1 best solutions below

14
zer00ne On

Update 2

You need to go to JSHint and debug your code frequently. In Update 2 I have corrected as much as I care to. Before I can help you any further, you need to address the issues marked with a ❓.

Mark Description Corrected
There are 12 typos, even if there was only one ❌ it would FUBAR whatever follows it. Yes
There are 5 questionable areas which will most likely need to be addressed because it may cause problems later on, or is pointless, etc. No
Areas that could be modified to be more efficient, succinct, etc. No
Areas that are modified to be more efficient, succinct, etc. Yes

In Example D I placed where my code should go. There's always the chance that none of it may work because of Django. I have no idea, but on the surface it doesn't look debilitating.

Concerning <form>s, they are the the backbone of most interactive webpages but not necessary if you have other means such as fetch(). That is true in your case which means that you don't trigger "submit" events and you don't need a type="submit" unless there's something Django does that I'm ignorant to.

It isn't obvious to me how the HTML layout is exactly. I'm not certain whether there's one button (good) or multiple buttons (a very critical detail to know from the start).

Example D

document.addEventListener('DOMContentLoaded', bindText);

function bindText(e) {
  const required = [...document.querySelectorAll(".inputs")];
  required.forEach(input => input.oninput = checkText);
}

function checkText(e) {
  /*❌: = Does not belong at end of line */
  const required = [...document.querySelectorAll(".inputs")];
  /*❌: `${id}` NOT "${id}"|BUT `${id}` === id */
  /*❓: Is id declared elsewhere? If not, use it's actual "idOfButton". 
        If you have multiple submit buttons then you should reassess
        your strategy. 
  */
  const button = document.getElementById(id);
  /*❌: = missing */
  button.disabled = !required.every(input => input.value.length > 0);
}

/*❌: '...' or "..." NOT "...' */
document.addEventListener('DOMContentLoaded', function() {
  /*❌: = : ' missing */
  const quizButton = document.querySelectorAll('.quiz_button');
  for (const button of quizButton) {
    /*⭕: In event handlers/listeners if it is a named function that's
          defined separately, remove the ()
    */
    button.addEventListener('click', (event) => checkQuiz);
  }
});
/*⭕: Event handlers pass the (e)vent object by default */
function checkQuiz(e) {
  const id = e.target.id;
  /*❌: / missing a \ needs to be escaped: \\ */
  /*❌: response* number 1 NOT letter l */
  let response1 = document.getElementById('id_question1').value.toUpperCase().replace(/\\/g, "");
  /*❌: / missing a \ needs to be escaped: \\ */
  let response2 = document.getElementById('id_question2').value.toUpperCase().replace(/\\/g, "");
  /*❓: Are sure there's a <div id="div">? */
  let div1 = document.getElementById('div');
  /*❌: '...' or '..." NOT '..." */
  let div2 = document.getElementById('div2');
  /*❓: var hoists to be global use let or const. In this case let 
        is the best choice
  */
  var correctAnswers = 0;

  fetch(`/check/${id}`)
    .then(response => response.json())
    .then(quiz => {
      /*❓: Is rightM declared elsewhere? If not,
             use let or const
      */
      rightM = "You got this correct. Great job!";

      let answ1 = quiz.answer1.toUpperCase().replace(/\s/g, "");
      if (answ1 === response1) {
        div1.innerHTML = rightM;
        div1.classList.add("correct");
        correctAnswers++;
      } else {
        div1.innerHTML = `The correct answer is ${quiz.answer1}. Nice try!`;
        div1.classList.add("incorrect");
      }

      let answ2 = quiz.answer1.toUpperCase().replace(/\s/g, "");
      if (answ2 === response2) {
        div2.innerHTML = rightM;
        div2.classList.add("correct");
        correctAnswers++;
      } else {
        div2.innerHTML = `The correct answer is ${quiz.answer2}. Nice try!`;
        div2.classList.add("incorrect");
      }
      /*: DRY = Don't Reapeat Yourself. Iterate through quiz.answer1 with
             a loop or an array method.
      */
      //this repeats 8 more times

      console.log(correctAnswers);
      let score = document.getElementById("score");
      /*❌: ` missing */
      /*❌: $' '{ space between $ and { breaks interpolation */
      score.innerHTML = `
      Your score is ${correctAnswers}.Great job!:)`;
      score.classList.add("score_style");
      /*❓: is newPoint declare elsewhere? If not use let or const */
      newPoints = correctAnswers * 10;

      let currentUser = parseInt(document.getElementById("user_id").value);
      let currentPoints = parseInt(document.getElementById("user_points").value);
      let numOfPoints = currentPoints + newPoints;
      /*❌: ` missing*/
      fetch(`/points/${currentUser}`, {
        method: "PUT",
        body: JSON.stringify({
          points: numOfPoints
        })
      });
    });
}

Update 1

As requested examples B and C include JavaScript that'll enable a disabled <button> when all <input>have text.

If you have a <form> wrapped around everything (which is very simple to do if you don't), add required to all of the <input> -- that'll prevent the <form> from being submitted should there be any blank <input>s (see Example A).

Example A - Has no JavaScript or CSS -- just HTML. The <button> isn't disabled, but the <form> will not allow itself to be submitted if any <input> is blank.

Example B - Has a <form> and uses HTMLFormElement and HTMLFormControlsCollection interfaces which facilitates the use of form controls.

Example C - Is a less elegant solution involving standard DOM manipulation.

Note that in Example B the event handler is bound to the <form> and that it is shared by each <input>. Should any form control be added to the <form> later on, the event handling will apply to it as well. In Example C each <input> has the input bound to them individually. Should any form control be added later on they'll need to have their own event handler/listener attached after they have been created.

Examples B and C have commented details

Example A

<form>
  <input required value='XXXXXXXXXXX'><br>
  <input required value='XXXXXXXXXXX'><br>
  <input required placeholder='Enter text here'><br>
  <button>Submit</button>
</form>

Example B

// Reference the <form>
const F = document.forms[0];
// Reference all form controls
const fc = F.elements;
// Collect all name='required' into a HTMLCollection, convert it into an array
const required = [...fc.required];
// Reference the <button>
const done = fc.done;
/**
 * Bind input event to <form>...
 * ...enable the <button> if .every() <input> has a value
 */
F.oninput = e =>
  done.disabled = !required.every(input => input.value.length > 0);
<form>
  <input name='required' value='XXXXXXXXXXX'><br>
  <input name='required' value='XXXXXXXXXXX'><br>
  <input name='required' placeholder='Enter text here'><br>
  <button id='done' disabled>Done</button>
</form>

Example C

// Collect all .required into a NodeList, convert it into an array
const required = [...document.querySelectorAll('.required')];
// Reference the <button>
const done = document.getElementById('done');
/**
 * Bind the input event to each <input>...
 * ...<button> is enabled if .every() <input> has a value
 */
required.forEach(input => input.oninput = e =>
  done.disabled = !required.every(input => input.value.length > 0));
<form>
  <input class='required' value='XXXXXXXXXXX'><br>
  <input class='required' value='XXXXXXXXXXX'><br>
  <input class='required' placeholder='Enter text here'><br>
  <button id='done' disabled>Done</button>
</form>