I have used JavaScript to disable a button until all the inputs are filled, but I also want to keep the button disabled if any input is filled only with whitespace. (I am not using a form because it interferes with my other code.) How do I check if the inputs have any character other than whitespace?
JavaScript code that disables the button once the inputs are filled:
document.addEventListener('DOMContentLoaded', function(){
const required = document.querySelectorAll('.input');
//gets all the quiz_buttons
const quizButton = document.querySelectorAll('.quiz_button');
for (const i of required){
i.addEventListener("input", checkSubmit);
}
for (const button of quizButton){
button.disabled = true;
button.addEventListener('click', (event) =>
check_quiz(event.target.id));
}
function checkSubmit(){
let isValid = true;
for (const i of required){
isValid = isValid && !!i.value;
}
for (const button of quizButton){
button.disabled = !isValid;
}
}
});
CodePudding user response:
You can use every
to check if each input.value has length, and - using a regex - doesn't have just whitespace.
// Cache the elements up-front. We coerce `input` to
// and array so we can use `every` later
const container = document.querySelector('.container');
const inputs = [...document.querySelectorAll('input')];
const button = document.querySelector('button');
// Hang on listener off the parent component - using
// event delegation we can catch events from its child
// elements when they're fired and "bubble up" the DOM
container.addEventListener('input', handleInput);
function handleInput(e) {
// Check that the child element that fired the
// event is an input element
if (e.target.matches('input')) {
// Use `every` to iterate over the inputs.
// `every` returns a boolean - if the condition
// isn't met it returns false, otherwise true.
// Here the condition checks that the input has
// length, and isn't just all whitespace
let valid = inputs.every(input => {
return input.value.length
&& !/^\s $/.test(input.value)
});
// Disable/enable the button based
// on the result
button.disabled = valid ? false : true;
}
}
<section >
<input />
<input />
<input />
<input />
</section>
<button disabled>Submit</button>
Additional documentation
The regex reads:
^ - start of string
\s - one or more spaces
$ - end of string
CodePudding user response:
This can be done in both CSS/HTML and with JavaScript; though the HTML and CSS approach does make use of the :has()
pseudo-class (currently limited to Safari, Chrome and Edge (the latter two behind a flag)):
// I don't like typing, so I use these helpers to cache the
// document, and aliases for querySelector() and querySelectorAll():
const D = document,
get = (sel, context = D) => context.querySelector(sel),
getAll = (sel, context = D) => [...context.querySelectorAll(sel)];
// if the browser's CSS interface does not have support for the
// selector of ":has(a)":
if (!CSS.supports('selector(:has(a))')) {
// we retrieve all <label> elements:
const labels = getAll('label'),
// and all <input> elements:
inputs = getAll('input'),
// declare a function to check validity:
checkValidity = () => {
// we retrieve the <button> element:
const button = get('button'),
// we retrieve all <input> elements (this isn't strictly
// necessary, since we have a reference outside of the
// function and we could use that variable here, but
// this is my preference as it allows for code to be
// moved around:
inputs = getAll('input'),
// we check that every <input> is valid using the validityState
// interface; if so the state is set to (Boolean) true,
// otherwise it's set to false:
state = inputs.every((el) => el.validity.valid);
// here we invert the state (if all <input> elements are valid
// we want the disabled state of the <button> to be false):
button.disabled = !state;
};
// iterating over the <input> elements using Array.prototype.forEach():
inputs.forEach(
// arrow function passes the current <input> into the function
// as the 'el' argument; here we use EventTarget.addEventListener()
// to bind the checkValidity() function - note the deliberate lack
// of parentheses - as the event-handler for the 'input' event:
(el) => el.addEventListener('input', checkValidity)
);
}
/* A simple reset, to remove default margin and padding,
and to have element-sizes calculated by including
border-sizes and padding in the declared width: */
*,::before,::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
form {
/* using flex-box layout: */
display: flex;
/* flex-direction: row;
flex-wrap: wrap; */
flex-flow: row wrap;
/* setting a gap of 0.5em between adjacent elements: */
gap: 0.5em;
/* margin - in left-to-right, top-to-bottom languages
such as English - this is equivelent to margin-top
and margin-bottom: */
margin-block: 1em;
/* margin - in left-to-right, top-to-bottom languages
such as English - this is equivelent to margin-left
and margin-right: */
margin-inline: auto;
/* setting a preferred width of 60vw, but preventing
sizes smaller than 12em and limiting the maximum
to 1000px at most; inline-size is equivalent to
width (in English) */
inline-size: clamp(12em, 60vw, 1000px);
}
label {
/* vertically aligning the content */
align-items: center;
/* setting the border style:
border-width: 1px;
border-style: solid;
border-color: var(--color-validity);
this last sets the color of the border
to be equal to the CSS custom property:
*/
border: 1px solid var(--color-validity);
/* using flex-box layout on its contents: */
display: flex;
/* sizing the <label> to be 100% width: */
flex-basis: 100%;
/* setting its contents to
flex-direction: row;
flex-wrap: wrap;
to allow those contents to be displayed in rows,
and to be able to dynamically resize and wrap: */
flex-flow: row wrap;
/* allows the element to grow to occupy maximum space
possible: */
flex-grow: 1;
gap: inherit;
padding: 0.25em;
/* using a transition of 0.3seconds on the scale
property: */
transition: scale 0.3s ease-in;
}
label:focus-within {
/* increases the size of the element when the
input descendant is focused: */
scale: 1.1;
/* the element will be scaled from its center
point: */
transform-origin: 50% 50%;
}
/* a <label> that has a :valid descendant will
set the --color-validity property to lime: */
label:has(:valid) {
--color-validity: lime;
}
/* a <label> that has an :invalid descendant will
set the --color-validity property to red: */
label:has(:invalid) {
--color-validity: red;
}
/* styling the placeholder of the <input> elements */
::placeholder {
font-style: italic;
}
.titleText {
flex-basis: 30%;
flex-grow: 1;
white-space: nowrap;
}
/* an element with the class of "titleText" within a
<label> which has a descendant with the "required"
attribute will have the content of '*' in its
::after pseudo-element, and that will be colored
according the --color-validity custom-property: */
label:has([required]) .titleText::after {
content: '*';
color: var(--color-validity);
}
input {
flex-basis: 50%;
flex-grow: 1;
padding: 0.25em;
}
<form action="#">
<label>
<span >Enter character first name</span>
<!-- to make use of HTML validation, both the 'required' attribute must be set and a pattern
supplied; the 'pattern' attribute is a string containing a regular expression,
below we have a regular expression to match:
1. ^ starts at the beginning of the string,
2. [A-Z] one upper-case letter in the range from A-Z inclusive, leading white-space
is not considered valid,
3. [a-zA-Z-] a letter of either upper or lower case between a-z, or a hyphen,
4. {2,} repeated at least twice (but with no maximum),
5. continuing until the end of the string (trailing white-space is not valid) -->
<input type="text" placeholder="John, Lucy, Rashda..." pattern="^[A-Z][a-zA-Z-]{2,}$" required>
</label>
<label>
<span >Enter character family name</span>
<input type="text" placeholder="John, Lucy, Rashda..." pattern="^[a-zA-Z]'?[a-zA-Z]{2,}$" required>
</label>
<button type="submit">Submit</button>
</form>
Note that while the CSS won't perform to style the <label>
elements or their descendants, in browsers that don't support the :has()
selector, the HTML validation of the <input>
elements will still function if, for any reason, the JavaScript doesn't run.
References: