Search code examples
javascripthtmljquery

Why is submit button getting enabled even when required inputs are empty?


I've got a form and a submit button that needs to be disabled until all required inputs are filled in. Some inputs are text, and some are dropdowns. I've read through lots of threads on how to handle this, and I think I'm doing it correctly, but for some reason the submit button gets enabled as soon as the user fills out the text input, even though the dropdown inputs are still empty.

For the text input, I'm checking whether the string length is 0. For the dropdowns, I'm checking whether the value is "select". Why does the submit button get enabled once the text input string is > 0, even when the dropdown values are still "select"? Here's a working codepen and see code below as well.

// Input Variables
let createDigestModal = $("#create-digest-modal");
let createDigestFormContainer = $("#create-digest-modal-form-container");
let createDigestModalSubmitBtn = $("#create-digest-modal-btn");

// ========================================================================
// Toggle the submit button to disabled / enabled based on required inputs
// ========================================================================
const enableSubmitBtn = () => {
  // Create digest form
  $(createDigestFormContainer).on("keyup click", () => {
    let createDigestInputs = createDigestFormContainer.find(".required");
    let requiredCreateDigestInputs = true;

    for (let i = 0; i < createDigestInputs.length; i++) {
      console.log(createDigestInputs[i].value);
      if (createDigestInputs[i].value == "") {
        requiredCreateDigestInputs = false;
      }
    }

    createDigestModalSubmitBtn.prop("disabled", !requiredCreateDigestInputs);
  });
};

// Invoke Function to toggle submit button
enableSubmitBtn();
.row {
  margin-bottom: 1rem;
}

/* Required Fields */

.required-field::after {
  content: "*";
  color: red;
  margin-left: 2px;
}

.required-field-margin-left::after {
  content: "*";
  color: red;
  margin-left: -2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- Create Digest Modal -->
<div class="modal fade" id="create-digest-modal" tabindex="-1" aria-labelledby="create-digest-modal-label">

  <form id="create-digest-modal-form-container">
    <!-- Project Input -->
    <div class="row">
      <div class="col-40">
        <label class="required-field" for="create-digest-modal-project-input">Project</label>
      </div>
      <div>
        <select class="form-select required" id="create-digest-modal-project-input" type="select">

          <option value="select">Select</option>
          <option value="1">Project 1</option>
          <option value="2">Project 2</option>
        </select>
      </div>
    </div>
    <!-- /Project Input -->

    <!-- name input -->
    <div class="row">
      <div id="create-digest-name-input">
        <label id="create-digest-modal-name-input-label" for="create-digest-modal-name-input" class="required-field">Digest Name</label>
      </div>

      <div>
        <input name="create-digest-modal-name-input" type="text" id="create-digest-modal-name-input" class="required" />
      </div>
    </div>
    <!-- /name input -->

    <!-- Type input -->
    <div class="row">
      <div class="col-40">
        <label class="required-field" for="create-digest-modal-type-input">Type</label>
      </div>
      <div class="col-75 flex">
        <select class="form-select required" aria-label="create-digest-modal-type-input" id="create-digest-modal-type-input" type="select">
          <option value="select">Select</option>
          <option value="D">Daily</option>
          <option value="W">Weekly</option>
        </select>
      </div>
    </div>
    <!-- /Type input -->

    <!-- include summary input -->
    <div id="create-digest-modal-summary-input-container" class="row">
      <div class="col-40">
        <label class="form-check-label" for="create-digest-modal-summary-input">
          Include Summary
        </label>
      </div>
      <div class="col-75 flex">
        <input checked class="form-check-input" type="checkbox" id="create-digest-modal-summary-input" />
      </div>
    </div>
    <!-- /include summary input -->
  </form>

  <!-- Close/Update Buttons -->
  <div class="modal-footer">
    <button type="button" class="btn btn-secondary modal-close-btn" data-bs-dismiss="modal" id="create-modal-close-btn">
      Close
    </button>
    <button type="button" id="create-digest-modal-btn" class="btn btn-success" disabled>
      Add Digest
    </button>
  </div>

</div>
<!-- Create Digest Modal -->


Solution

  • When the value is select, the condition createDigestInputs[i].value.length == "" fails. Since you're combining the conditions with &&, they both have to be true to disable the button, but that can't happen.

    The usual solution is to use an empty value for the unselected option, so it's consistent with text inputs.

    Rather than using the click event, use change to detect changes in a dropdown.

    Checking for an empty input value should be done with .value == "", not .value.length == "". The only reason the latter works is because comparing a number to a string automatically converts the string to a number, and the empty string converts to 0.

    You can break out of the for loop once you find an unset input.

    You don't need to put createDigestFormContainer and createDigestModalSubmitBtn inside $(). They're already jQuery objects.

    // Input Variables
    let createDigestModal = $("#create-digest-modal");
    let createDigestFormContainer = $("#create-digest-modal-form-container");
    let createDigestModalSubmitBtn = $("#create-digest-modal-btn");
    
    // ========================================================================
    // Toggle the submit button to disabled / enabled based on required inputs
    // ========================================================================
    const enableSubmitBtn = () => {
      // Create digest form
      createDigestFormContainer.on("keyup change", () => {
        let createDigestInputs = createDigestFormContainer.find(".required");
        let requiredCreateDigestInputs = true;
    
        for (let i = 0; i < createDigestInputs.length; i++) {
          console.log(createDigestInputs[i].value);
          if (
            createDigestInputs[i].value == ""
          ) {
            requiredCreateDigestInputs = false;
            break;
          }
        }
    
        createDigestModalSubmitBtn.attr("disabled", !requiredCreateDigestInputs);
    
      });
    };
    
    // Invoke Function to toggle submit button
    enableSubmitBtn();
    .row {
      margin-bottom: 1rem;
    }
    
    /* Required Fields */
    
    .required-field::after {
      content: "*";
      color: red;
      margin-left: 2px;
    }
    
    .required-field-margin-left::after {
      content: "*";
      color: red;
      margin-left: -2px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <!-- Create Digest Modal -->
    <div class="modal fade" id="create-digest-modal" tabindex="-1" aria-labelledby="create-digest-modal-label">
    
      <form id="create-digest-modal-form-container">
        <!-- Project Input -->
        <div class="row">
          <div class="col-40">
            <label class="required-field" for="create-digest-modal-project-input">Project</label>
          </div>
          <div>
            <select class="form-select required" id="create-digest-modal-project-input" type="select">
    
              <option value="">Select</option>
              <option value="1">Project 1</option>
              <option value="2">Project 2</option>
            </select>
          </div>
        </div>
        <!-- /Project Input -->
    
        <!-- name input -->
        <div class="row">
          <div id="create-digest-name-input">
            <label id="create-digest-modal-name-input-label" for="create-digest-modal-name-input" class="required-field">Digest Name</label>
          </div>
    
          <div>
            <input name="create-digest-modal-name-input" type="text" id="create-digest-modal-name-input" class="required" />
          </div>
        </div>
        <!-- /name input -->
    
        <!-- Type input -->
        <div class="row">
          <div class="col-40">
            <label class="required-field" for="create-digest-modal-type-input">Type</label>
          </div>
          <div class="col-75 flex">
            <select class="form-select required" aria-label="create-digest-modal-type-input" id="create-digest-modal-type-input" type="select">
              <option value="">Select</option>
              <option value="D">Daily</option>
              <option value="W">Weekly</option>
            </select>
          </div>
        </div>
        <!-- /Type input -->
    
        <!-- include summary input -->
        <div id="create-digest-modal-summary-input-container" class="row">
          <div class="col-40">
            <label class="form-check-label" for="create-digest-modal-summary-input">
              Include Summary
            </label>
          </div>
          <div class="col-75 flex">
            <input checked class="form-check-input" type="checkbox" id="create-digest-modal-summary-input" />
          </div>
        </div>
        <!-- /include summary input -->
      </form>
    
      <!-- Close/Update Buttons -->
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary modal-close-btn" data-bs-dismiss="modal" id="create-modal-close-btn">
          Close
        </button>
        <button type="button" id="create-digest-modal-btn" class="btn btn-success" disabled>
          Add Digest
        </button>
      </div>
    
    </div>
    <!-- Create Digest Modal -->