How to create flexible input-fields in Javascript? - javascript

I am having problems to create the following situation:
I want to create inputfields, where I can write "1", "2" or "3" in any order, but each number is only allowed to be writen once.
Repeating one of those three numbers and writing other numbers than those three in the inputfields should be considered bad.
What do I need to add to the code?
a = 1
b = 2
c = 3
L = a
L = b
L = c
function F1() {
feedBack = document.getElementById("feedBack");
an = document.getElementById("userAnswer");
L = document.getElementById("L").textContent;
if (an == L) {
feedBack.textContent = "good";
} else {
feedBack.textContent = "bad";
}
}
<input id="userAnswer" type=text>
<input id="userAnswer" type=text>
<input id="userAnswer" type=text>
<button onclick="F1()">check</button>
<label id="L"> </label>
<p id="feedBack"> </p>

It's worth mentioning (especially for the more novice JavaScripters reading this) that, as with most programming problems, there are lots of ways to correctly satisfy the requirements from the original question.
I know this isn't Code Review.SE, but you might benefit from some feedback on the example you provided, if you're interested:
The variables a, b, and c are never used in any particularly useful way, and detract from the human readability of your code. You should probably remove their declarations entirely.
You set the variable L three times consecutively. This doesn't do very much at all, considering also L is overridden once F1() is executed. You should probably remove these unnecessary assignments entirely.
The HTML specification is clear that ID values should be unique across the entire document space; anything else is invalid and can lead to headaches and undocumented behavior down the line, especially when JavaScript comes into play. In the simplified example, there really isn't any need to assign them IDs at all (see solution).
Relatively minor, but inline event handling (i.e., using the onclick attribute) is generally regarded as an outdated concept. Instead, use the addEventListener() paradigm to make your code easier to interpret and debug.
The function name F1() isn't particularly descriptive to what the function actually does when called. A future developer maintaining your code would have a less difficult time discerning what this method does if it was named something more descriptive, like validateForm(), or even just validate().
To satisfy your requirements, you might look to write something more like what I've got below. In a nutshell, when the validate() function is run, the following actions are taken:
Instantiates validNumbers, an array of the valid inputs to be validated against he fields.
Instantiates inputFields which evaluates to a NodeList iterable which can be looped.
Instantiates feedbackElement, a reference to your <p> node in the DOM.
Loops all the inputFields, and checks if the value of the current field is in the validNumbers array.
If the validation is successful, the code removes the valid value from the validNumbers array (as it's already been used and can't be used again) and proceed to validate the next field.
If the validation is unsuccessful, the code automatically sets the text of your feedback element to "bad" and breaks out of the validation loop.
Once all three input fields have been validated, the code checks to see if there are any remaining elements of validNumbers left. If there are, not all elements were used and the feedback is once again set to "bad". Otherwise, the validation checks out and the feedback element's contents are set to "good".
function validate() {
var validNumbers = [1, 2, 3];
var inputFields = document.querySelectorAll("input[type='text']");
var feedbackElement = document.querySelector("p#feedBack");
for (field of inputFields) {
var fieldValue = parseInt(field.value);
if (validNumbers.includes(fieldValue)) {
// valid number & not encountered yet
validNumbers.splice(validNumbers.indexOf(fieldValue), 1);
} else {
// invalid number or already used
feedbackElement.innerText = "bad";
break; // break out of `for` loop, as there's already no possible way for the provided numbers to be "good"
}
}
if (validNumbers.length === 0) {
// all numbers were used
feedbackElement.innerText = "good";
} else {
// not all numbers were used
feedbackElement.innerText = "bad";
}
}
document.querySelector("button").addEventListener('click', function() {
validate();
});
<input type=text>
<input type=text>
<input type=text>
<button>check</button>
<p id="feedBack"> </p>

You could try to loop over the input elements and then validate each element, in the loop save the value of the input in a set/array for each input so with this we would have a cache to check the input field values.
function validate() {
let numberEntered = new Set();
let allowedValues = [1, 2, 3];
let inputStatus = false;
let feedBack = document.getElementById("feedBack");
let inputEle = document.querySelectorAll(".number-field");
for (let input of inputEle) {
let value = parseInt(input.value);
if (allowedValues.includes(value) && !numberEntered.has(value)) {
numberEntered.add(value)
} else {
inputStatus = true;
break;
}
}
if (inputStatus) {
feedBack.textContent = "Bad";
} else {
feedBack.textContent = "good";
}
}
<input id="userAnswer1" class="number-field" type=text>
<input id="userAnswer2" class="number-field" type=text>
<input id="userAnswer3" class="number-field" type=text>
<button onclick="validate()">check</button>
<label id="L"> </label>
<p id="feedBack"> </p>

Related

Javascript For Loop will not increment

I am trying to use a for loop to push values entered by a user into an array. But, for some reason, the loop will not increment to push the next value into the array but will instead overwrite the first location. This is the HTML used to get the user's input below.
<div class="total-budget-fields">
<h3>Enter Budget</h3>
<input type="number" placeholder="$1000" id="budget">
<input type="button" onclick="addNum();" class="btn hit" id="budget" value="Calculate">
</div>
And this here is the javascript function linked to the button below.
addNum = () => {
// console.log('addNum');
var budgetArray = [];
var budget = document.getElementById('budget').value;
for (i=0; ; i++) {
if (budget.trim() == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
break;
}
}
console.log(budgetArray);
console.log(i);
}
I tried using a while loop as an alternative which didn't work. Any and all help is welcomed, thank you in advanced!
Like already mentioned in the comments, the loop makes non sense and you dont need an index variable like i. Instead make the array global and just push new values. This will increase the size of the array automatically. If budgetArray is in the scope of your function, it is created on every call of this function.
var budgetArray = [];
addNum = ()=>{
var budget = document.getElementById('budget').value;
if (budget.trim() == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
}
}
Also in your markup file two elements has the same id budget. You should fix that and make your id's unique across your whole document. It currently works because if there is more than one element with the same id, getElementById will just give you the first one.
The first problem is that on every click you are reassigning you budgetArray to be an empty array. This is why you will always have only one item in the array.
You have to cache your array outside the addSum function. As your budget container will not change during the time, it is a good idea to cache it as well.
Also, you do not need for loop for this task at all. So something like this will do the job.
var budgetArray = [];
var budgetContainer = document.getElementById('budget');
addNum = () => {
const budget = budgetContainer.value.trim();
if (budget == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
}
}
console.log(budgetArray);
console.log(i);

My if statement gives me the wrong output

window.onload = start;
function start() {
document.getElementById("kalk").onclick = find;
find(1, 9999);
}
function find(min, max) {
var factor = document.getElementById("tall").value;
var factor2 = document.getElementById("tall2").value;
var x = factor * factor2;
document.getElementById("utskrift").innerHTML = x;
if (x >= min && x <= max) {
document.getElementById("msg").innerHTML = "Number is in interval."
} else {
document.getElementById("msg").innerHTML = "Number is not in interval."
}
}
<h2>Gang to tall</h2>
T1 <input type="number" id="tall" /> T2 <input type="number" id="tall2" />
<button id="kalk">Finn tall i intervall</button> Sum: <span id="utskrift"></span>
<p id="msg"></p>
So by reading this code.. what Im trying to do is have two inputs where i multiply the numbers typed in them. In my "Find()" Parameter i have two arguments that says the numbers should be between 1-9999. In my "function find" i called these arguments min and max. Further down the code Im asking if the output is between these numbers are between min and max give "Number is in interval". The problem is that when i even when the numbers are in these arguments i get my else statement. Is there anyway to fix this or put an input felt in the parameter?
Thanks
You are attaching the function find directly to the click event listener. The function is expecting two parameters min and max:
function find(min, max)
But when a click happens, it recieves, being an event listener, only one parameter which is the event object. Thus min is going to be an event object, and max will be undefined and your if statement won't work. You can check this out by logging min and max inside find to the console.
Wrap the function find call in another function and attach the latter as the event listener:
document.getElementById("kalk").onclick = function(event) { // the event listener function recieves the event object as the only parameter (not that argument event here is not necessary, I just added it for explanation)
find(1, 90000); // when this function get executed (when a click happens) then call find with proper parameters
}
Well, first you have to remember that the "T" in HTML stands for "text". There is only one data type in HTML... strings. When you get a value from HTML and want to use it as a number in JavaScript, you have to convert it to a number.
Next, you don't really have your functions organized correctly. find shouldn't just run as soon as the page is loaded. You want it to run when the button is clicked. And that way, the min and max values are passed at the moment you need them.
Lastly, you HTML statically says "Sum" when, you are in fact calculating a product.
See other best practice items in the comments.
<html>
<head>
</head>
<body>
<h2>Gang to tall</h2>
T1 <input type="number" id="tall"/>
T2 <input type="number" id="tall2"/>
<button id="kalk">Finn tall i intervall</button>
Product: <span id="utskrift"></span>
<p id="msg"></p>
<!-- It's a good idea to place your scripts just before the closing
body tag. That way, the HTML has been loaded by the time the
JavaScript runs. -->
<script>
document.getElementById("kalk").onclick = result;
// Get your DOM references just once so you don't have to re-scan the DOM
// for them every time the button is clicked.
// Also, just reference the elements themselves, not properties (.value) of
// the element because if you just reference the property and later decide
// you need some other property, you'll have to re-scan the DOM for the same
// element again. This way, you maintain a reference to the element and can
// get any property you need at any time.
var factor = document.getElementById("tall");
var factor2 = document.getElementById("tall2");
var product = document.getElementById("utskrift");
var msg = document.getElementById("msg");
function find(min,max){
// All values coming from HTML are strings. You should always
// explicitly convert them to numbers when numbers are expected.
var x = parseInt(factor.value, 10) * parseInt(factor2.value,10);
// .innerHTML is for when you are assigning a string that contains HTML.
// It tells the HTML parser to parse the string for HTML. If you are not
// including HTML, use .textContent, which doesn't do this extra parsing.
product.textContent = x;
if (x >= min && x <= max) {
msg.textContent = "Number is in interval."
} else {
msg.textContent = "Number is not in interval."
}
}
function result(){
find(1, 9999);
}
</script>
</body>
</html>

How do I reference text boxes using a variable

So I am making a small quiz on JavaScript for the first time. I wanted the user to be able to select between 3 options(a, b and c) by entering the letter in a text box and clicking a button to check the answer. A text will then appear underneath the text box to show whether the user entered the right option or not.
I have managed to make this work but since there are multiple questions, I wanted to use a for loop to loop through each text box (I named each text box "0", "1" ...) but I cannot reference them using i. How can I do it?
Here is my JavaScript:
var answer = ["a", "b", "c"];
var results = "results"
function check() {
for (i = 1; i = 4; i++) {
var input = document.getElementById(i).value;
if (input == answer[parseInt(i-1)]) {
document.getElementById(results.concat(i)).innerHTML = "Correct";
}
else {
document.getElementById(results.concat(i)).innerHTML = "Wrong";
}
}
}
Here is the HTML (I repeated the same code for each question with a different ID):
<input type="text" id="0" value="a, b or c"><br>
<input type=button value="Check" onClick="check()"><br>
Result: <span id="results0"></span><br><br>
This should work:
There are a few things wrong with your original code
Wrong:
for (i = 1; i = 4; i++)
usually we start our iterations on 0 index, also the middle portion of the for loop should return truthy which means it should evaluate to true or false "i = 4" will never be either because = is an assignment operator, you should use evaluation operators >, <, >=, ==, etc.
for (i = 0; i < 3; i++) {
var input = document.getElementById(i).value;
if (input == answer[i]) {
document.getElementById("results" + i).innerHTML = "Correct";
}
else {
document.getElementById("results" + i).innerHTML = "Wrong";
}
}
Additionally you were using "result" like a variable in this:
results.concat(i)
So results would need to be a variable that contains the string "results" which I doubt is the case. so what we are telling the getElementById method by doing it this way
document.getElementById("results" + i)
is to find an element with an ID of "results" plus the index of the loop eg. id="result0", or id="result1" etc.
If you have all of these within a <div> or div tag you can reference them using :nth-child notation. This works by referencing the nth child from the beginning.
$("div:nth-child(i)");
With the html you provided your for loop is incorrect.
Since you started your input at 0 you want to start looping at 0.
for (i = 0; i <= 2; i++) { //with three values 0,1,2
...
}
Your <span> tag also need to be closed with a </span>
Looping through id's feels very dirty to me. From a maintenance and readability standpoint, I recommend you loop through a collection of Nodes instead.
I believe that you are looking for:
document.getElementsByClassName("someClass")[0];
Where [0] Would be the index of the element whos class is shared.
Here is a code sample I wrote for you to reference using textboxes and outputting the values into a div within a for loop.
https://jsfiddle.net/4ocnyy38/1/
You can dynamically create inputbox and result span and alot dynamic ids to them and then check the correct answer on click of button
Javascript:
var answer = ["a", "b", "c"];
var results = "results";
setTimeout(function(){
var injectData='';
for(var i=0;i<3;i++){
injectData+="<input type='text' id='id_"+i+"' placeholder='a, b or c'/> Result: <span id='results_"+i+"'></span><br/>";
}
document.getElementById('inject').innerHTML=injectData;
},500);
function check() {
for (i = 0; i <3; i++) {
var input = document.getElementById('id_'+i).value;
if (input == answer[i]) {
document.getElementById("results_"+i).innerHTML = "Correct";
}
else {
document.getElementById("results_"+i).innerHTML = "Wrong";
}
}
}
HTML:
<div id="inject">
</div>
<input type=button value="Check" onClick="check();"><br>
Demo: https://jsfiddle.net/9ea46of5/

How to modify the algorithm to reduce the delay?

I have a lot of inputs like this where user enters value:
<INPUT TYPE=TEXT NAME="Milk" ONKEYUP="convcase(document.convert.Milk.value)">
<INPUT TYPE=TEXT NAME="Buckwheat" ONKEYUP="convcase(document.convert.Buckwheat.value)">
and a lot of calculation like this:
document.convert.Fe.value = BuckwheatFe * document.convert.Buckwheat.value + PeaFe * document.convert.Pea.value + MilkFe * document.convert.Milk.value + ...
document.convert.Hexadecanoic.value = BuckwheatHexadecanoic * document.convert.Buckwheat.value + PeaHexadecanoic * document.convert.Pea.value + MilkHexadecanoic * document.convert.Milk.value + ...
so the result after calculation shows dynamically and when the program has hundreds of products the delay between input and count is too large. I calculate all products: milk, buckwheat... even if the user does not enter their value.
Could you advise me how to modify the algorithm to reduce the delay?
I would approach it something like the following. The inputs that need to be used in the calculation can be denoted with a class, say "itemValue", and retrieved once then cached. This supposes that they don't change.
The markup can look like:
<form name="convert">
Milk: <input class="itemValue" name="Milk" onkeyup="convcase(this)"><br>
Buckwheat: <input class="itemValue" name="Buckwheat" onkeyup="convcase(this)"><br>
Fe value: <input name="Fe"><br>
Hex value: <input name="Hexadecanoic"><br>
</form>
Things like the Fe and Hexadecanoic values can also be cached. It also helps if the collection of nodes is converted to an array so that built–in array functions can be used. These may be slower than using a for loop, so if they are, convert the reduce call to a loop.
// Helper to convert a NodeList to an array so built-in methods can be used
function toArray(list) {
var i = list.length, arr = [];
while (i--) {
arr[i] = list[i];
}
return arr;
}
The function that does the actual work:
var convcase = (function() {
// Cache stuff in a closure. Note that the names of each factor set
// must match the form control name where the related total is written
var factors = {
Fe: {Buckwheat:0.5, Milk:0.75},
Hexadecanoic: {Buckwheat:0.6, Milk:0.82}
};
var nodes;
return function (el) {
// Only get nodes the first time, assumes they don't change, and convert to Array
// This can be done before validation as it's not dependent on it
nodes = nodes || toArray(el.form.querySelectorAll('input.itemValue'));
// Validate el.value here and deal with invalid values.
// Only proceed if it's a valid value
// For each set of factors, do all the calculations and write totals to form
for (var factor in factors) {
// Get the factor set
var set = factors[factor];
// Write the total to the related form control
el.form[factor].value = nodes.reduce(function(sum, node){
sum += node.value * set[node.name];
return sum;
}, 0);
}
};
}());
I wouldn't do this on keyup, I'd wait for the change or blur events so calculation was only done when there was a good chance the user has finished for the moment, otherwise there may be lots of useless calculations.
Store elements in the array and use looping . Also you can store previous values (old one in some other array ) and before performing calculation compare the old value with the new one . If the value changed then only do it .
I know a standard way to handle this. The point is: you don't want every key stroke to trigger the algorithm. You want to wait for the client to stop typing (for a while); then you trigger the algorithm.
(EDIT: by the way, read the comments below. You might be helped just by changing "onkeyup" by "onchange")
With a clearTimeout/setTimeout combination you can do exactly that. I set the 'typing time' to 400ms, feel free to change this value.
<script>
var timer = null;
var interval = 400; // feel free to change this value
function imputChanged(elm) {
// if any previous key stroke happend less than 400ms ago; clearTimeout will cancel that request.
// only the last request will trigger executeAlgorithm().
clearTimeout(timer);
timer = setTimeout(function() {
executeAlgorithm(elm);
},
interval
);
}
function executeAlgorithm(elm) {
// do what ever you have to do here. As an example, I show the value in a div
document.getElementById('messages').innerHTML = elm.value;
}
</script>
<input onkeyup="imputChanged(this)">
<div id="messages"></div>
(EDIT: it looks like onkeyup works better on IE than onimput; I now set the trigger to onkeyup)

JavaScript array.sort() Issue

drafting up a quick listing tool to list local kids baseball teams. Takes a couple of inputs and writes to a text field. There's some validation and whatnot too, but that's out of scope and doesn't seem to be impacting things.
Problem is, I'm having trouble figuring out how to "capture" the existing text, add the new inputs and sort the whole lot, before writing the new result to the paragraph element (effectively replacing it).
So far I have:
var LeagueTeams = [];
var IndividualTeam = '';
LeagueTeams.push(document.forms[0].TeamName.value);
LeagueTeams.push(document.getElementById('TeamList')
LeagueTeams = LeagueTeams.sort();
for (j = 0; j < LeagueTeams.length; j++) {
IndividualTeam = LeagueTeams.pop();
IndividualTeam = IndividualTeam + '' + \n;
document.forms[0].TeamName.value += IndividualTeam;
}
What I end up getting is my input, and then an array of my input PLUS the previous contents, with a couple of line breaks. Setting the operator to = instead of =+ stops it from printing to the array at all.
i.e.
Enter: a
Text area: a
Then enter: b
Text area: a ab
(etc)
OK, now that we have a better idea of what you're trying to do, here's some code that will do that:
HTML:
<label>Enter Team Name: <input id="newTeam" type="text"></label>
<button id="add">Add</button><br>
All Teams:<br>
<textarea id="allTeams" rows="40" cols="40"></textarea>
Javascript (plain javascript, no framework, called after page is loaded):
var teamList = ["Dodgers", "Mets", "Giants"];
document.getElementById("add").onclick = function() {
var input = document.getElementById("newTeam");
if (input.value) {
teamList.push(input.value);
}
updateTeamList();
input.value = "";
}
function updateTeamList() {
teamList.sort();
var o = document.getElementById("allTeams");
o.value = teamList.join("\n");
}
updateTeamList();
And, you can see it working here: http://jsfiddle.net/jfriend00/HkhsL/
Comments on your existing code:
I'm not sure I understand what you're trying to do overall, but do you realize that this loop is going to have problems:
for (j = 0; j < LeagueTeams.length; j++) {
IndividualTeam = LeagueTeams.pop();
IndividualTeam = IndividualTeam + '' + \n;
document.forms[0].TeamName.value += IndividualTeam;
}
Each time you do LeagueTeams.pop() you are reducing the length of the array and you're continually comparing to LeagueTeams.length in the for loop. This will only get half way through the array because each time through the loop, you increment j and decrement LeagueTeams.length which means you'll only get half way through the array.
If you intend to iterate all the way through the array in your for loop, you should use this version that gets the length once initially and simplifies the code in the loop:
for (j = 0, len = LeagueTeams.length; j < len; j++) {
document.forms[0].TeamName.value += LeagueTeams.pop() + '\n';
}
or perhaps even better, this version that doesn't even use j:
while (LeagueTeams.length > 0) {
document.forms[0].TeamName.value += LeagueTeams.pop() + '\n';
}
Then further, I see that you're trying to use LeagueTeams.sort() on an array that has both strings in it and DOM object references. What are you trying to do with that sort because the built-in sort function does a lexigraphical sort (e.g. alpha) which will do something odd with a DOM reference (probably sort by whatever toString() returns which may be object type)?
If you want to sort the input by team name, then you would need to put both team name and the DOM reference into an object, insert that object into the array as one unit and then use a custom sort function that would sort by the name in the object. As your code is written above, you see to be using document.getElementById('TeamList') which is the same for all teams so I'm not sure why you're putting it into the array at all.
If you can show your HTML and a more complete version of your code, we could help further. What you have above is just a non-working piece of code and we don't know what your HTML looks like that it's trying to operate on.
FYI, there are several syntax errors in the code you posted, so this can't be running code:
Missing paren at the end of this: LeagueTeams.push(document.getElementById('TeamList'))
Missing quotes around \n: IndividualTeam = IndividualTeam + '' + '\n';
If you are just trying to make a list of the teams, try something like:
<script type="text/javascript">
function addTeam(form) {
var para = document.getElementById('teamList');
var teams = para.innerHTML.split(/<br\s*[\\]?>/);
teams.push(form.teamName.value);
para.innerHTML = teams.sort().join('<br>');
}
</script>
<form action="">
<input type="text" name="teamName">
<input type="button" value="Add team" onclick="addTeam(this.form)">
</form>
<p id="teamList"></p>
You may be using different elements or layout, but the strategy should be about the same. If you are making a set of options for a select, things are a little easier.

Categories