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.
Update to original question: based on the answers here, it was clear to me that I should be using built-in form validation and then adding any javascript needed to customize the UI experience, rather than handling all form validation in javascript. Since I'm already using Bootstrap, I decided it would be best to refactor my code to use their form validation methods. If you're reading this question, and you happen to be using Bootstrap, check out their docs. Otherwise both the answers below will work for Javascript / jQuery.
// 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=".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 -->
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.
Update to original question: based on the answers here, it was clear to me that I should be using built-in form validation and then adding any javascript needed to customize the UI experience, rather than handling all form validation in javascript. Since I'm already using Bootstrap, I decided it would be best to refactor my code to use their form validation methods. If you're reading this question, and you happen to be using Bootstrap, check out their docs. Otherwise both the answers below will work for Javascript / jQuery.
// 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 -->
Share
Improve this question
edited 2 days ago
Mickey Vershbow
asked Feb 7 at 21:55
Mickey VershbowMickey Vershbow
3097 silver badges24 bronze badges
7
|
Show 2 more comments
2 Answers
Reset to default 1When 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 -->
If you use the build-in form validation you can use the method reportValidity() on the form to test if the button should be enabled. Give each of the required fields the attibute required
, and if you don't like the validity message on each of the fields you can disable this by listening for the invalid
event and then call preventDefault()
on that event.
Btw. use the name attribute on all form fields and buttons. And use underscore _
instead of hyphen -
for names so that you can use the dot syntax to get the elements (like form.create_digest_modal_btn
).
const form01 = document.forms['create-digest-modal-form-container'];
form01.addEventListener('input', e => {
let form = e.target.form;
form.create_digest_modal_btn.disabled = !form.reportValidity();
});
form01.addEventListener('invalid', e => {
e.preventDefault();
}, true);
.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;
}
<!-- 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" id="create-digest-modal-project-input" type="select" required>
<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" 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" aria-label="create-digest-modal-type-input" id="create-digest-modal-type-input" type="select" required>
<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 -->
<!-- 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" name="create_digest_modal_btn" class="btn btn-success" disabled>
Add Digest
</button>
</div>
</form>
</div>
<!-- Create Digest Modal -->
createDigestInputs[i].value.length == ""
will never be true for the dropdowns, since the value isselect
. – Barmar Commented Feb 7 at 22:04select
. – Barmar Commented Feb 7 at 22:05||
instead of&&
in the condition. But then if the user entersselect
into the text box they won't be able to submit. – Barmar Commented Feb 7 at 22:060
? It happens to "work" because the empty string is converted to a number, which results in0
. – Barmar Commented Feb 7 at 22:08<dialog>
and.showModal()
see doc here – Mister Jojo Commented Feb 7 at 22:30