Task
Who among us is not familiar with that unpleasant situation when you carefully fill out a form, enter data, then, hoping to click the "Submit" button, you find out that something went wrong and all your efforts were in vain? There's a solution for this - instant validation with JavaScript!
An excellent user experience is the key to success. Form validation with HTML cannot provide the level of UX (user experience) required by web development standards. JavaScript validation comes to the rescue. It provides instant feedback while filling out the form and neatly hints at what needs to be corrected before the form is submitted.
Ready Solution
Example of standard HTML markup for a form:
<form class="form" name="form" method="POST" novalidate> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Name:</span> <input type="text" id="input__name" class="form__type-input" placeholder="Ivan" pattern="^[a-zA-Zа-яА-ЯЁё \-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="name-error" required > </label> <span class="form__error input__name-error" id="name-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Surname:</span> <input type="text" id="input__surname" class="form__type-input" placeholder="Vasilievich" pattern="^[a-zA-Zа-яА-ЯЁё\s\-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="surname-error" required > </label> <span class="form__error input__surname-error" id="surname-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Email:</span> <input type="email" id="input__e-mail" class="form__type-input" placeholder="menyaet.professiyu@ivan.com" aria-describedby="email-error" required > </label> <span class="form__error input__e-mail-error" id="email-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Age:</span> <input type="number" id="input__age" class="form__type-input" placeholder="40" min="18" max="100" aria-describedby="age-error" required > </label> <span class="form__error input__age-error" id="age-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__checkbox-label"> <input type="checkbox" id="input__checkbox" class="form__type-checkbox" checked aria-describedby="checkbox-error" required /> <span class="form__type-checkbox-title"> I agree to be a king </span> </label> <span class="form__error input__checkbox-error" id="checkbox-error" aria-live="polite"></span> </div> <button type="submit" class="button" aria-describedby="empty-error" > Submit </button> <span class="form__empty-error" id="empty-error" aria-live="assertive" > </span></form>
<form class="form" name="form" method="POST" novalidate > <div class="form__field-container"> <label class="form__field"> <span class="form__label">Name:</span> <input type="text" id="input__name" class="form__type-input" placeholder="Ivan" pattern="^[a-zA-Zа-яА-ЯЁё \-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="name-error" required > </label> <span class="form__error input__name-error" id="name-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Surname:</span> <input type="text" id="input__surname" class="form__type-input" placeholder="Vasilievich" pattern="^[a-zA-Zа-яА-ЯЁё\s\-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="surname-error" required > </label> <span class="form__error input__surname-error" id="surname-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Email:</span> <input type="email" id="input__e-mail" class="form__type-input" placeholder="menyaet.professiyu@ivan.com" aria-describedby="email-error" required > </label> <span class="form__error input__e-mail-error" id="email-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__field"> <span class="form__label">Age:</span> <input type="number" id="input__age" class="form__type-input" placeholder="40" min="18" max="100" aria-describedby="age-error" required > </label> <span class="form__error input__age-error" id="age-error" aria-live="polite" > </span> </div> <div class="form__field-container"> <label class="form__checkbox-label"> <input type="checkbox" id="input__checkbox" class="form__type-checkbox" checked aria-describedby="checkbox-error" required /> <span class="form__type-checkbox-title"> I agree to be a king </span> </label> <span class="form__error input__checkbox-error" id="checkbox-error" aria-live="polite"></span> </div> <button type="submit" class="button" aria-describedby="empty-error" > Submit </button> <span class="form__empty-error" id="empty-error" aria-live="assertive" > </span> </form>
JavaScript code for validating all fields of the form:
const form = document.querySelector('.form')const inputList = Array.from(form.querySelectorAll('.form__type-input'))const checkboxElement = form.querySelector('.form__type-checkbox')const buttonElement = form.querySelector('.button')const formErrorElement = form.querySelector('.form__empty-error')startValidation()function startValidation() { toggleButton() form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() }) })}function checkInputValidity(inputElement) { if (inputElement.validity.patternMismatch) { inputElement.setCustomValidity(inputElement.dataset.errorMessage) } else { inputElement.setCustomValidity(checkLengthMismatch(inputElement)) }}function checkLengthMismatch(inputElement) { if (inputElement.type !== 'text') { return '' } const valueLength = inputElement.value.trim().length if (valueLength < inputElement.minLength) { return `Minimum number of characters: ${inputElement.minLength}` } return ''}function hasInvalidInput() { return ( inputList.some(inputElement => !inputElement.validity.valid) || !checkboxElement.validity.valid )}function toggleErrorSpan(inputElement, errorMessage){ const errorElement = document.querySelector(`.${inputElement.id}-error`) if (errorMessage) { inputElement.classList.add('form__type-input-error') errorElement.textContent = errorMessage errorElement.classList.add('form__error-active') } else { inputElement.classList.remove('form__type-input-error') errorElement.textContent = '' errorElement.classList.remove('form__error-active') }}function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') formErrorElement.textContent = '' }}function formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage}
const form = document.querySelector('.form') const inputList = Array.from(form.querySelectorAll('.form__type-input')) const checkboxElement = form.querySelector('.form__type-checkbox') const buttonElement = form.querySelector('.button') const formErrorElement = form.querySelector('.form__empty-error') startValidation() function startValidation() { toggleButton() form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() }) }) } function checkInputValidity(inputElement) { if (inputElement.validity.patternMismatch) { inputElement.setCustomValidity(inputElement.dataset.errorMessage) } else { inputElement.setCustomValidity(checkLengthMismatch(inputElement)) } } function checkLengthMismatch(inputElement) { if (inputElement.type !== 'text') { return '' } const valueLength = inputElement.value.trim().length if (valueLength < inputElement.minLength) { return `Minimum number of characters: ${inputElement.minLength}` } return '' } function hasInvalidInput() { return ( inputList.some(inputElement => !inputElement.validity.valid) || !checkboxElement.validity.valid ) } function toggleErrorSpan(inputElement, errorMessage){ const errorElement = document.querySelector(`.${inputElement.id}-error`) if (errorMessage) { inputElement.classList.add('form__type-input-error') errorElement.textContent = errorMessage errorElement.classList.add('form__error-active') } else { inputElement.classList.remove('form__type-input-error') errorElement.textContent = '' errorElement.classList.remove('form__error-active') } } function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') formErrorElement.textContent = '' } } function formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage }
CSS styles to be used during validation:
/* To change the border color of the form element during validation */.form__type-input-error { border: 1px solid #FF8630; background-color: rgb(255 134 48 / 0.1);}/* To display the span element with the error */.form__error-active { display: block;}/* To disable the submit button */.button-inactive { cursor: default; background-color: rgb(211 211 211 / 0.6);}
/* To change the border color of the form element during validation */ .form__type-input-error { border: 1px solid #FF8630; background-color: rgb(255 134 48 / 0.1); } /* To display the span element with the error */ .form__error-active { display: block; } /* To disable the submit button */ .button-inactive { cursor: default; background-color: rgb(211 211 211 / 0.6); }
Analysis of the Solution
First, we inform the browser that it should not validate the form in the standard way by adding the novalidate
attribute to the <form>
tag.
<form class="form__field" novalidate> <!-- Form content --></form>
<form class="form__field" novalidate> <!-- Form content --> </form>
Markup
Look at the example of the markup of the form element to better understand how validation works.
We add attributes:
type
- defines the expected type of data in the field.placeholder
- provides a hint to the user about what data to enter.required
- indicates the necessity of filling out the field.
We link the input field and the <span>
with an error using identifiers and CSS classes. We assign an identifier to <input>
and give the same class for <span>
, adding '-error' at the end. This will allow finding the <span>
in the DOM
by such a scheme: document
. To make this connection between the field and its error clear for users of assistive technologies, we associate them through the aria
attribute in the field and the button along with the id
of the same value in <span>
. To ensure assistive technologies automatically announce them, we additionally add another ARIA attribute aria
.
We configure the validation parameters. Standard attributes like maxlength
/minlength
can be used here, as well as non-standard attributes such as pattern
with regular expressions. The latter allows configuring more precise and specific rules for input fields.
More about pattern
: Although in most cases, standard validation messages are sufficient, there are times when more specific requirements for input fields are necessary. For instance, the case where only letters of the Latin and Cyrillic alphabet, hyphens, and spaces are allowed. This set of symbols is not provided by standard validation, making custom validation necessary. We use a regular expression and store the custom error message in a specially created data attribute - 'data-error-message'. More about data attributes can be read in the documentation for data-* attributes.
<label class="form__field"> <span class="form__label">Name:</span> <input type="text" id="input__name" class="form__type-input" placeholder="Ivan" pattern="^[a-zA-Zа-яА-ЯЁё \-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="name-error" required ></label><span class="form__error input__name-error" id="name-error" aria-live="polite"></span>
<label class="form__field"> <span class="form__label">Name:</span> <input type="text" id="input__name" class="form__type-input" placeholder="Ivan" pattern="^[a-zA-Zа-яА-ЯЁё \-]+$" data-error-message="Allowed symbols are Latin, Cyrillic, hyphens, and spaces." aria-describedby="name-error" required > </label> <span class="form__error input__name-error" id="name-error" aria-live="polite" > </span>
JavaScript
First, we gather all the necessary DOM elements for validation:
const form = document.querySelector('.form')const inputList = Array.from(form.querySelectorAll('.form__type-input'))const checkboxElement = form.querySelector('.form__type-checkbox')const buttonElement = form.querySelector('.button')const formErrorElement = form.querySelector('.form__empty-error')
const form = document.querySelector('.form') const inputList = Array.from(form.querySelectorAll('.form__type-input')) const checkboxElement = form.querySelector('.form__type-checkbox') const buttonElement = form.querySelector('.button') const formErrorElement = form.querySelector('.form__empty-error')
The start
function initiates the validation process. It adds an event listener for the entire form on the submit
event, where event
is used to prevent the form's standard behavior upon submission. For more information, read "Working with Forms" (/js/deal-with-forms/).
Event handlers input
, blur
, and focus
are assigned to each input field. Any changes in the input fields activate the functions check
and toggle
. When the focus is lost, the toggle
function is activated, and when focus is gained, the error message is reset using toggle
.
A dedicated handler change
is added for the checkbox, which will trigger the functions toggle
and toggle
when such an event occurs. All of these functions will be written later.
function startValidation() { form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() })}
function startValidation() { form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() }) }
The check
function utilizes the JavaScript Validity
object to check each input field. If the field is not valid, an error message is displayed.
The validity
object can be seen when accessing the validity
key of the input element (input
). It looks like this:
{ badInput: false, customError: false, patternMismatch: false, rangeOverflow: false, rangeUnderflow: false, stepMismatch: false, tooLong: false, tooShort: false, typeMismatch: false, valid: true, valueMissing: false,}
{ badInput: false, customError: false, patternMismatch: false, rangeOverflow: false, rangeUnderflow: false, stepMismatch: false, tooLong: false, tooShort: false, typeMismatch: false, valid: true, valueMissing: false, }
For an input field to be considered valid, its property input
must equal true
. This property becomes true
when all other properties in the validity
object are false
. More about validity
and the meaning of each key in this object can be found here: MDN Web Docs - ValidityState.
In the check
function, the validity
object and its pattern
key for custom errors, as well as valid
for standard checks, are used.
First, we check whether a specific pattern has been set for the input field and whether a minimum length is established. If a pattern is defined and does not match the entered data, the set
function is used to pass the custom error message stored in the data
attribute. If the entered data matches the pattern, the function check
also checks the length of the entered data, stripped of spaces. If the message exceeds the established character limit and is not empty, the error message is not transmitted; otherwise, the user receives a message with the minimum required number of characters.
The toggle
function implements standard validation: if input
is false
, an error message is displayed, and if true
, the error is removed.
function checkInputValidity(inputElement) { if (inputElement.validity.patternMismatch) { inputElement.setCustomValidity(inputElement.dataset.errorMessage) } else { inputElement.setCustomValidity(checkLengthMismatch(inputElement)) }}function checkLengthMismatch(inputElement) { if (inputElement.type !== 'text') { return '' } const valueLength = inputElement.value.trim().length if (valueLength < inputElement.minLength) { return `Minimum number of characters: ${inputElement.minLength}` } return ''}function toggleInputError(inputElement) { if (!inputElement.validity.valid) { toggleErrorSpan(inputElement, inputElement.validationMessage) } else { toggleErrorSpan(inputElement) }}
function checkInputValidity(inputElement) { if (inputElement.validity.patternMismatch) { inputElement.setCustomValidity(inputElement.dataset.errorMessage) } else { inputElement.setCustomValidity(checkLengthMismatch(inputElement)) } } function checkLengthMismatch(inputElement) { if (inputElement.type !== 'text') { return '' } const valueLength = inputElement.value.trim().length if (valueLength < inputElement.minLength) { return `Minimum number of characters: ${inputElement.minLength}` } return '' } function toggleInputError(inputElement) { if (!inputElement.validity.valid) { toggleErrorSpan(inputElement, inputElement.validationMessage) } else { toggleErrorSpan(inputElement) } }
The toggle
function is simple: it disables the button when invalid fields are found and re-enables it when all fields are correctly filled. The has
function checks the input fields and checkbox for errors and returns true
or false
, based on whether invalid data has been detected.
Disabling the submit button is a risky technique. It is important to consider various user behaviors. To avoid situations where the user does not understand the reason for the button being disabled, we take the following measures:
- We apply the class
button
, which changes the color of the button to a less bright color, signaling to the user that pressing is impossible.- inactive - We add
cursor
property through this class, changing the cursor's shape to a prohibition symbol.: not - allowed; - When clicking the submit button or pressing it with the keyboard, we show an error explaining the reason for the disabling. We implement this using JavaScript.
- If invalid data is entered into one of the fields, the user receives feedback on the error made after losing focus on the field or after removing the marker from the required checkbox.
- As soon as the user inputs valid data, the button becomes active.
- To make the disabled button noticeable to users navigating the site using the Tab key, we add the attribute
aria
to the button.- disabled
We also remind that when blocking the submit button, it is essential to ensure that the filling requirements are reasonable and achievable by all users. We should avoid establishing excessively strict conditions for the data entered by the user. For instance, a user may find their name too long for a specified limit in the form of 10 characters, which would make submitting the form impossible and limit access to your product.
function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') formErrorElement.textContent = '' }}function hasInvalidInput() { return ( inputList.some(inputElement => !inputElement.validity.valid) || !checkboxElement.validity.valid )}function formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage}
function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') formErrorElement.textContent = '' } } function hasInvalidInput() { return ( inputList.some(inputElement => !inputElement.validity.valid) || !checkboxElement.validity.valid ) } function formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage }
.button-inactive { cursor: not-allowed; background-color: rgb(211 211 211 / 0.6);}.button-inactive:hover { background-color: rgb(211 211 211 / 0.2); border: 2px solid transparent;}.form__empty-error { padding: 10px 0; font-size: 18px; color: #FF8630;}
.button-inactive { cursor: not-allowed; background-color: rgb(211 211 211 / 0.6); } .button-inactive:hover { background-color: rgb(211 211 211 / 0.2); border: 2px solid transparent; } .form__empty-error { padding: 10px 0; font-size: 18px; color: #FF8630; }
Finally, we need to activate the elements with errors. If an input field is invalid, the script shows the pre-prepared element with an error message. If the field becomes valid, the message disappears. This is where we needed the trick where we created the error class using the following template: id of input element + '
function toggleErrorSpan(inputElement, errorMessage){ const errorElement = document.querySelector(`.${inputElement.id}-error`) if (errorMessage) { inputElement.classList.add('form__type-input-error') errorElement.textContent = errorMessage errorElement.classList.add('form__error-active') } else { inputElement.classList.remove('form__type-input-error') errorElement.textContent = '' errorElement.classList.remove('form__error-active') }}
function toggleErrorSpan(inputElement, errorMessage){ const errorElement = document.querySelector(`.${inputElement.id}-error`) if (errorMessage) { inputElement.classList.add('form__type-input-error') errorElement.textContent = errorMessage errorElement.classList.add('form__error-active') } else { inputElement.classList.remove('form__type-input-error') errorElement.textContent = '' errorElement.classList.remove('form__error-active') } }
Additionally, we take care of the empty form error when clicking or pressing the keyboard on the button.
const formErrorElement = form.querySelector('.form__empty-error')function startValidation() { form.addEventListener('submit', (event) => { event.preventDefault() // Show error if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() })}function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') // Remove error text formErrorElement.textContent = '' }}// Here we store and add text to the needed containerfunction formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage}
const formErrorElement = form.querySelector('.form__empty-error') function startValidation() { form.addEventListener('submit', (event) => { event.preventDefault() // Show error if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() }) } function toggleButton() { if (hasInvalidInput()) { buttonElement.classList.add('button-inactive') buttonElement.setAttribute('aria-disabled', 'true') } else { buttonElement.classList.remove('button-inactive') buttonElement.setAttribute('aria-disabled', 'false') // Remove error text formErrorElement.textContent = '' } } // Here we store and add text to the needed container function formError() { const errorMessage = 'Please fill in all fields to submit the form.' formErrorElement.textContent = errorMessage }
Our code is ready! Don't forget:
- To call the function
toggle
at the very beginning of theButton ( ) start
function so that the button is already disabled before entering any characters.Validation ( ) - To invoke the
start
function.Validation ( )
// Call the functionstartValidation()function startValidation() { toggleButton() form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() })}
// Call the function startValidation() function startValidation() { toggleButton() form.addEventListener('submit', (event) => { event.preventDefault() if (hasInvalidInput()) { formError() inputList.forEach((inputElement) => { checkInputValidity(inputElement) toggleInputError(inputElement) }) toggleInputError(checkboxElement) } }) inputList.forEach((inputElement) => { inputElement.addEventListener('input', () => { checkInputValidity(inputElement) toggleButton() }) inputElement.addEventListener('blur', () => { toggleInputError(inputElement) }) inputElement.addEventListener('focus', () => { toggleErrorSpan(inputElement) }) }) checkboxElement.addEventListener('change', () => { toggleInputError(checkboxElement) toggleButton() }) }