I'm trying to validate an input field. When i try to submit without filling in something, it gives me the error i made: please start your question with: will i ever. So i'm trying to check wether the text that the user types into the field, starts with: will i ever.
However, when i type a single (or more) random character(s), it just submits the form. I want it to check if the input starts with those fixed tree words, otherwise, no submission.
{
const handleSubmitForm = e => {
const $form = e.currentTarget;
if (!$form.checkValidity()) {
e.preventDefault();
const field = $form.querySelector('.question_field');
showValidationInfo(field);
//$form.querySelector('.error').innerHTML = 'Some errors occured';
} else {
console.log('Form is valid => submit form');
}
};
const showValidationInfo = field => {
console.log(field);
let message;
if (field.validity.valueMissing) {
message = 'Please fill in a question starting with: Will i ever';
}
if (field.validity.typeMismatch) {
message = 'Type not right';
}
if (field.validity.rangeOverflow) {
const max = field.getAttribute('max');
message = 'Too big, max ${max}';
}
if (field.validity.rangeUnderflow) {
const min = field.getAttribute('min');
message = 'Too small, min ${min}';
}
if (field.validity.tooShort) {
const min = field.getAttribute('minlength');
message = 'Too short, minimum length is ${min}';
}
if (field.validity.tooLong) {
const max = field.getAttribute('maxlength');
message = 'Too long, maximum length is ${max}';
}
if (!field.value.toLowerCase().startsWith("will i ever")) {
message = 'Start your question with: Will i ever';
}
if (message) {
field.parentElement.querySelector('.error').textContent =
message;
field.parentElement.querySelector('.error').style.color = "red";
}
};
const handeInputField = e => {
const $field = e.currentTarget;
if ($field.checkValidity()) {
$field.parentElement.querySelector('.error').textContent = '';
if ($field.form.checkValidity()) {
$field.form.querySelector('.error').innerHTML = '';
}
}
};
const handeBlurField = e => {
const $field = e.currentTarget;
showValidationInfo($field);
};
const addValidationListeners = fields => {
fields.forEach($field => {
$field.addEventListener('input', handeInputField);
$field.addEventListener('blur', handeBlurField);
});
};
const init = () => {
const $form = document.querySelector('form');
$form.noValidate = true;
$form.addEventListener('submit', handleSubmitForm);
const fields = $form.querySelectorAll('.input');
addValidationListeners(fields);
};
init();
}
<div class="name_wrapper">
<form autocomplete="off" class="form_question" action="answer.html">
<label class="name question" for="name">Ask me a question</label>
<div class="question_wrapper">
<p class="error">Start your question with: Will i ever...</p>
<input class="field question_field" type="text" name="question" placeholder="Will i ever..." value="" required>
</div>
<input id="button" class="answr-btn btn-question" type="submit" value="answer it!">
<input autocomplete="false" name="hidden" type="text" style="display:none;">
</form>
</div>
This line makes no sense:
const fields = $form.querySelectorAll('.input');
There are no HTML elements with class="input" in your form.
Did you mean $form.querySelectorAll('input')?
The problem is how you are handling the validation, the key is in this line if (!$form.checkValidity()) { this will not check if your string starts with Will i ever you have to do it manually before the if, here you have an alternative solution:
{
const handleSubmitForm = e => {
const $form = e.currentTarget;
const field = $form.querySelector('.question_field');
//here we validate the form manually
const message = showValidationInfo(field);
//if a message is found we show the error on the DOM, if is undefined we have no errors and we can submit the form
if (message) {
e.preventDefault();
$form.querySelector('.error').innerHTML = message;
$form.querySelector('.error').style.color = "red";
} else {
console.log('Form is valid => submit form');
}
};
const showValidationInfo = field => {
if (field.validity.valueMissing) {
return 'Please fill in a question starting with: Will i ever';
}
if (field.validity.typeMismatch) {
return 'Type not right';
}
if (field.validity.rangeOverflow) {
const max = field.getAttribute('max');
return 'Too big, max ${max}';
}
if (field.validity.rangeUnderflow) {
const min = field.getAttribute('min');
return 'Too small, min ${min}';
}
if (field.validity.tooShort) {
const min = field.getAttribute('minlength');
return 'Too short, minimum length is ${min}';
}
if (field.validity.tooLong) {
const max = field.getAttribute('maxlength');
return 'Too long, maximum length is ${max}';
}
if (!field.value.toLowerCase().startsWith("will i ever")) {
return 'Start your question with: Will i ever';
}
return undefined;
};
const handeInputField = e => {
const $field = e.currentTarget;
if ($field.checkValidity()) {
$field.parentElement.querySelector('.error').textContent = '';
if ($field.form.checkValidity()) {
$field.form.querySelector('.error').innerHTML = '';
}
}
};
const handeBlurField = e => {
const $field = e.currentTarget;
showValidationInfo($field);
};
const addValidationListeners = fields => {
fields.forEach($field => {
$field.addEventListener('input', handeInputField);
$field.addEventListener('blur', handeBlurField);
});
};
const init = () => {
const $form = document.querySelector('form');
$form.noValidate = true;
$form.addEventListener('submit', handleSubmitForm);
const fields = $form.querySelectorAll('.input');
addValidationListeners(fields);
};
init();
}
<div class="name_wrapper">
<form autocomplete="off" class="form_question" action="answer.html">
<label class="name question" for="name">Ask me a question</label>
<div class="question_wrapper">
<p class="error">Start your question with: Will i ever...</p>
<input class="field question_field" type="text" name="question" placeholder="Will i ever..." value="" required>
</div>
<input id="button" class="answr-btn btn-question" type="submit" value="answer it!">
<input autocomplete="false" name="hidden" type="text" style="display:none;">
</form>
You have uncommented backtick at occured `;
Related
I need to get the value of a input to be displayed in real time but I need to show some default text if the Input is empty or only spaces, but I cant seem to get it to work
const [productName, setProductName] = useState("");
const defaultProductName = "Name";
const handleChange = (event) => {
setProductName(event.target.value);
const inputValue = event.target.value;
const inputId = document.getElementById("productNameInput").innerHTML;
if (event.target.value.length == 0) {
document.getElementById("productNameID").innerHTML = defaultProductName;
} else {
document.getElementById("productNameID").innerHTML = inputValue;
}
};
<div className="productName" id="productNameID">
{defaultProductName}
</div>
<form>
<input type="text" id="productNameInput" onChange={handleChange} />
</form>
you can try:
if(document.getElementByID("productNameInput").value == " " | document.getElementByID("productNameInput").value.length == 0){
console.log("Input is empty")
}
I have a form with first name, last name and email, and i need to make an alert if a field is empty. I made an alert but it comes up for every field that is empty.
This is the js:`
document.querySelector("#book").addEventListener("click", () => {
let inp = document.querySelectorAll(".form-control");
for (let i = 0; i < inp.length; i++) {
if (inp[i].value == "") {
alert("All fields must be filled!");
}
}
});
`
The form-control class is on all the input fields.
If i leave all 3 inputs empty, the alert comes up 3 times.
Please help, my brain is stuck.
You can simply use an array to get all errors inside a single array and after the loop finish then you can give the final alert.
document.querySelector("#book").addEventListener("click", () => {
let inp = document.querySelectorAll(".form-control");
let errors = [];
for (let i = 0; i < inp.length; i++) {
if (inp[i].value == "") {
errors.push("Error "+ i);
}
}
if(errors != []){
alert("All fields must be filled!");
}
});
If you want a generic message, you can just display it once, and even stop the loop:
document.querySelector("#book").addEventListener("click", () => {
let inp = document.querySelectorAll(".form-control");
for (let i = 0; i < inp.length; i++) {
if (inp[i].value == "") {
alert("All fields must be filled!");
break; // <-- alert was shown, nothing else to do
}
}
});
If you want to show a single message, but specific to the missing field(s), you have to collect them in the loop, and show the single message after, something like
document.querySelector("#book").addEventListener("click", () => {
let inp = document.querySelectorAll(".form-control");
let missing=[];
for (let i = 0; i < inp.length; i++) {
if (inp[i].value == "") {
missing.push(inp[i].name);
}
}
if(missing.length) {
alert("Please fill the following fields too: "+missing.join());
}
});
you can use array.some on inputs get by querySelectorAll() to raise only one alert if one fields is not empty
document.querySelector("#book").addEventListener("click", () => {
let inputs = document.querySelectorAll(".form-control");
if ([...inputs].some(input => input.value === '')) {
alert("All fields must be filled !");
}
});
<input class="form-control" />
<input class="form-control" />
<button id="book">validate</button>
You can use the FormData object, then loop through all of the entries. If at least one of them is empty, send one alert and return out of the function.
<form>
<label for="firstName">First name:</label>
<input type="text" placeholder="first name" name="firstName" />
<label for="lastName">last name:</label>
<input type="text" placeholder="last name" name="lastName" />
<label for="email">First name:</label>
<input type="text" placeholder="email" name="email" />
<button type="submit">submit</button>
</form>
<script>
const form = document.querySelector('form');
const handleClick = (e) => {
e.preventDefault();
const formData = [...new FormData(e.target)];
for (const [key, val] of formData) {
if (!val) return alert('Failed to fill all fields.');
}
alert('Success!');
};
form.addEventListener('submit', handleClick);
</script>
Us a boolean variable to remember whether any field is empty while looking at them in the loop.
document.querySelector("#book").addEventListener("click", () => {
let anyEmpty = false;
let inp = document.querySelectorAll(".form-control");
for (let i = 0; i < inp.length; i++) {
if (inp[i].value == "") {
anyEmpty = true;
break;
}
}
if(anyEmpty) {
alert("All fields must be filled!");
}
});
I have an Virtual keyboard with Javascript the keyboard is typing in two inputs after reached maxlength it is focusing to second input. my problem is when i want to type in first input i should clicked to first input to focus it than typing with keyboard numbers
My question is How i can typing using this keyboard without clicking inside input, the first input should start typing immediately after i clicked on the buttons numbers
const maxLength = 7;
const firstInput = document.querySelector("#pin");
const secondInput = document.querySelector("#key");
const changedEvent = new Event("change")
let activeInput;
firstInput.addEventListener("focus", (event) => {
activeInput = event.target;
});
firstInput.addEventListener("change", (event) => {
console.log("i'm changing!");
if (firstInput.value.length >= maxLength) {
activeInput = secondInput;
secondInput.focus();
}
});
secondInput.addEventListener("focus", (event) => {
activeInput = event.target;
});
function resetNumber() {
if (!activeInput) {
console.log("pin");
return;
}
activeInput.value = "";
}
function setNumber(number) {
if (!activeInput) {
console.log("pin");
return;
}
activeInput.value = activeInput.value === number ? "" : (activeInput.value += number);
// manually tell the input that it has changed, so that the event listener defined above gets called. this usually only will happen with actual keyboard input
activeInput.dispatchEvent(changedEvent);
}
<button onclick="resetNumber()">Reset</button>
<button onclick="setNumber(0)">0</button>
<button onclick="setNumber(1)">1</button>
<button onclick="setNumber(2)">2</button>
<button onclick="setNumber(3)">3</button>
<button onclick="setNumber(4)">4</button>
<button onclick="setNumber(5)">5</button>
<button onclick="setNumber(6)">6</button>
<button onclick="setNumber(7)">7</button>
<button onclick="setNumber(8)">8</button>
<button onclick="setNumber(9)">9</button>
<br />
<input type="text" id="pin" />
<input type="text" id="key" />
<button id="reset" onclick="resetNumber()">Reset</button>
<br />
<input type="text" id="pin" />
<input type="text" id="key" />
<script>
const maxLength = 7;
const firstInput = document.querySelector('#pin');
const secondInput = document.querySelector('#key');
const resetBtn = document.querySelector('#reset');
for (let i = 9; i >= 0; i--) {
const numBtn = document.createElement('button');
numBtn.className = 'number';
numBtn.innerText = i;
resetBtn.parentElement.insertBefore(numBtn, resetBtn.nextSibling);
}
const numberBtns = document.querySelectorAll('.number');
const resetNumber = () => {
firstInput.setAttribute('value', '');
secondInput.setAttribute('value', '');
};
const setVal = (e) => {
const num = parseInt(e.target.innerText, 10);
if (firstInput.value.length <= maxLength) return firstInput.setAttribute('value', firstInput.value + num);
secondInput.setAttribute('value', secondInput.value + num);
};
numberBtns.forEach((btn) => btn.addEventListener('click', setVal));
</script>
I want to make it so when the user clicks the checkbox , the player Two input goes disabled. My problem is that the input remains disabled in both cases, doesn't matter if checkbox is checked or not.
const Initialization = (function() {
p1 = '';
p2 = '';
const playerOne = document.querySelector('#player1')
const playerTwo = document.querySelector('#player2')
const checkAI = document.querySelector('#computer')
const startButton = document.querySelector('#start')
startButton.addEventListener('click', () => {
p1 = Player(playerOne.value)
p2 = Player(playerTwo.value)
})
if (checkAI.checked = true) {
playerTwo.disabled = true;
} else {
playerTwo.disabled = false;
}
return {
p1,
p2,
}
})();
<label>Computer: <input type="checkbox" id="computer"></label><br/>
<input type="text" id="player1"><br/>
<input type="text" id="player2"><br/>
<input type="button" id="start" value="Start" />
worked by adding an eventListener to the checkbox.
checkAI.addEventListener('click',()=>{
if(checkAI.checked){
playerTwo.disabled = true;
}else{
playerTwo.disabled = false;
}
})
If you want to react on checkbox change you need to add event listener on this input. For example onclick or onchange. Take care to use comparaison operator in your if test checkAI.checked === true. You can find a JSFiddle
Event on checkbox input
You need an event handler and to test equality using == or ===
Here is a simpler version
const Initialization = function() {
const Player = str => console.log(str);
const playerOne = document.querySelector('#player1')
const playerTwo = document.querySelector('#player2')
const checkAI = document.querySelector('#computer')
const startButton = document.querySelector('#start')
let p1 = '';
let p2 = '';
startButton.addEventListener('click', () => {
p1 = Player(playerOne.value)
p2 = Player(playerTwo.value || "computer")
});
checkAI.addEventListener('click', (e) => {
const chk = e.target.checked;
playerTwo.disabled = chk;
if (chk) playerTwo.value="";
})
};
Initialization()
<label>Computer: <input type="checkbox" id="computer"></label><br/>
<input type="text" id="player1"><br/>
<input type="text" id="player2"><br/>
<input type="button" id="start" value="Start" />
You can accomplish this reactiveness by listening for the change event on the checkbox element and updating the input state accordingly:
const Initialization = (function() {
p1 = '';
p2 = '';
const playerOne = document.querySelector('#player1')
const playerTwo = document.querySelector('#player2')
const checkAI = document.querySelector('#computer')
const startButton = document.querySelector('#start')
startButton.addEventListener('click', () => {
p1 = Player(playerOne.value)
p2 = Player(playerTwo.value)
})
// listen for change event on checkbox
checkAI.addEventListener('change', () => {
// set playerTwo input to current checkbox state
playerTwo.disabled = checkAI.checked
})
return {
p1,
p2,
}
})();
<label>Computer: <input type="checkbox" id="computer"></label><br/>
<input type="text" id="player1"><br/>
<input type="text" id="player2"><br/>
<input type="button" id="start" value="Start" />
I want to generate input dynamically. Searched almost all samples and they generate inputs with button clicks. It is ok but i need something different. When user started to type in input then i want to create second input and when user typed to second then create third input like this..
My problem is when user typed inputs then it creates many inputs (letter counts=inputs). I must solve this issue immediately . I tried to some tricks but they could not work . Thanks for helping . You can check it in Codepen .
<div class="container1">
<button class="add_form_field">
Add New Field
<span style="font-size: 16px; font-weight: bold;">+ </span>
</button>
<div class="divInput firstChild">
<input type="text" autocomplete="off" name="mytext[]" />
</div>
</div>
$(document).ready(function () {
var max_fields = 10;
var wrapper = $(".container1");
var add_button = $(".add_form_field");
var inputs = document.querySelectorAll(".divInput");
let xs = [];
var x = 1;
$(add_button).click(function (e) {
e.preventDefault();
if (x < max_fields) {
x++;
$(wrapper).append(
'<div class="divInput"><input type="text" autocomplete="off" name="mytext[]"/></div>'
); //add input box
} else {
alert("You Reached the limits");
}
});
$(wrapper).on("keyup", "input", function (e) {
e.preventDefault();
if ($(this).val() == "") {
//when user cleaned input then delete it
$(this).parent("div").remove();
x--;
return;
} else if ($(this).val() != "") {
//dont use click check if length>0 then use this trick to add new input
$(add_button).trigger("click");
return;
}
if (x == 0) {
//if user tries to delete last input then create a new one
console.log("xx", x);
$(wrapper).append(
'<div class="divInput"><input type="text" autocomplete="off" name="mytext[]"/></div>'
); //add input box
x = 1;
console.log("xx", x);
return;
}
});
});
https://codepen.io/otontas11/pen/BaogrZR
First, you need to store in the current id in the input, make an event when user types and check the input id and check if is already added, if not then you proceed.
You need to attach the event to the body since you are dynamically creating elements.
Is this what you are looking for?
<div id="container">
<div class="generatedChild">
<input type="text" childId="1" autocomplete="off" name="mytext[]"/>
</div>
</div>
<script>
$(function(){
var max_fields = 10;
var wrapper = $('#container');
// attach the event to the Body, so all generated inputs will be listened by our event
$('body').on('input','.generatedChild input' ,function(e){
currentId = $(this).attr("childid");
childId = parseInt(currentId) + 1;
childOfThisInput = $(`input[childid=${childId}]`);
// check if is already added or if exceeds the maximum fields number
if(currentId > max_fields || childOfThisInput.length === 1) return;
$div = $("<div>", {class: "generatedChild"});
$input = $('<input>', {childid: childId, autocomplete:"off", name:"mytext[]", type: "text"});
$div.append($input);
wrapper.append($div);
});
});
</script>
Update:
If you need to let the user delete some of the generated inputs you can try something like this:
$(function(){
var max_fields = 10;
var wrapper = $('#container');
var inputsLength = 1;
// attach the event to the Body, so all generated inputs will be listened by our event
$('body').on('keydown','.generatedChild input' ,function(e){
currentId = $(this).attr("childid");
childId = parseInt(currentId) + 1;
childOfThisInput = $(`input[childid=${childId}]`);
if($(this).val() === "" && e.key === 8 && inputsLength > 1 )
return remove_input($(this));
// check if is already added or if exceeds the maximum fields number
if(inputsLength > max_fields || childOfThisInput.length === 1 || e.key === 8) return;
add_input(childId);
});
function add_input(childId){
$div = $("<div>", {class: "generatedChild"});
$input = $('<input>', {childid: childId, autocomplete:"off", name:"mytext[]", type: "text"});
$div.append($input);
wrapper.append($div);
++inputsLength;
}
function remove_input(element){
element.parent('.generatedChild').remove();
// let's focus the last generated input, so user won't need focus the input again
$('.generatedChild input:last').focus();
--inputsLength;
}
});
I know this is mostly an opinion, but IMO you shouldn't mix jQuery with react, they use fairly orthogonal ways to accomplish UI manipulation.
Here is a way I accomplished this design
const createInitialState = initialValues =>
initialValues.map(value => ({ id: uuidv4(), value })); // id used internally
const createOnChangeValues = values => values.map(({ value }) => value);
const InputList = ({ defaultValues = [], onChange }) => {
const renderRef = useRef(null);
const inputRef = useRef(null);
const [values, setValues] = useState(createInitialState(defaultValues));
const [inputFocused, setInputFocused] = useState(false);
const [inputValue, setInputValue] = useState("");
useEffect(() => {
if (renderRef.current) {
onChange && onChange(createOnChangeValues(values));
}
renderRef.current = true;
}, [onChange, values]);
const addValue = value => {
if (value) {
setValues(values => [...values, { id: uuidv4(), value }]);
setInputValue("");
}
};
const updateValue = index => e => {
const updateValue = e.target.value;
if (updateValue) {
setValues(values =>
values.map(({ id, value }, i) => ({
id,
value: index === i ? updateValue : value
}))
);
} else {
setValues(values => values.filter((_, i) => i !== index));
}
};
useEffect(() => {
if (!inputFocused && inputValue) {
addValue(inputValue);
inputRef.current.focus();
}
}, [inputFocused, inputValue]);
const submitHandler = e => {
e.preventDefault();
addValue(inputValue);
};
const resetHandler = e => {
e.preventDefault();
setInputValue("");
};
return (
<Fragment>
<form id="inputForm" onReset={resetHandler} onSubmit={submitHandler} />
{values.map(({ id, value }, i) => (
<div key={id}>
<input value={value} onChange={updateValue(i)} />
</div>
))}
<div>
<input
form="inputForm"
ref={inputRef}
type="text"
value={inputValue}
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
onChange={e => setInputValue(e.target.value)}
/>
</div>
{inputValue && (
<div>
<input />
</div>
)}
</Fragment>
);
};
The gist is you have some array of inputs currently being used, and you always render an extra blank input. As soon as user starts filling the new blank input it becomes part of the array of inputs upon losing focus. If any input becomes blank it is removed from the array. Much of the logic is to manage the swap from the new/empty input to one in the array and keep focus.
Usage
<InputList defaultValues={[... input values from state]} onChange={... values from inputs in array} />