Reorder Element value after Array Index change - javascript

var array = [];
$('input').click(function(){
var value = this.value;
if (array.indexOf(value) > -1) {
array.splice(array.indexOf(value));
$(this).next('span').html('');
return false;
}
array.push(value);
$(this).next('span').html(array.indexOf(value));
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<input type='button' value="x">
<span></span>
</div>
<div>
<input type='button' value="y">
<span></span>
</div>
In my Script here, I insert the index in the next span after input, So when you click x you get 0 and When you click y you get 1 and viceversa, The problem is When I click again on x which has index = 0,
What I expect is for the span of y to change to 0 instead of 1 since the key place changed after removing x index,
But that doesn't happen and the span stays 1 But if clicked it again, It changes to 0, How do I fix this problem?
EDIT: Added jQuery tag

Here's my go at updating everything using JQuery.
Something to note is that the splice function actually takes more arguments than just the index. If you don't supply the length parameter, it will delete everything in the array after the index.
var array = [];
$('input').click(function(){
var value = this.value
var result = true;
var i = array.indexOf(value);
if (i > -1) {
array.splice(i, 1);
result = false;
} else {
array.push(value);
}
updateSpans();
return result;
})
function updateSpans(){
$("input").each( function(){
var i = array.indexOf(this.value);
var newHTML = i;
if (i == -1){
newHTML = "";
}
$(this).next('span').html(newHTML);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<input type='button' value="x">
<span></span>
</div>
<div>
<input type='button' value="y">
<span></span>
</div>

You should give an ID to span elements to relate the buttons value with span elements, than you should only delete the element from array and not break the function.
Each time you click the button the content of span elements will be delete and created again, for each element that exist in array.
var array = [];
$('input').click(function(){
var value = this.value;
if (array.indexOf(value) > -1) {
array.splice(array.indexOf(value),1);
}else{
array.push(value);
}
$('span').html('');
$.each(array, function(i, item){
$('#'+item).html(array.indexOf(item));
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type='button' value="x">
<span id="x"></span>
<input type='button' value="y">
<span id="y"></span>

I created an outside function called 'updateDisplays' that will dynamically populate the spans as your array list changes...
// Make array[] global so that it can change across all the code
var array = [];
// bind all input elements (TODO: narrow input to specific type
// and/or class name) to a click event
$('input').click(function(){
// get the value of the input button
var value = this.value;
// test if the input's value already exists in the global
// array[]
var i = array.indexOf(value);
// if the element does already exist in the array[]...
if (i > -1) {
// remove it
array.splice(i, 1);
}else{
// if it is not in the array, add it
array.push(value);
}
// update the display output...
updateDisplays(array);
})
function updateDisplays(array){
// cycle through each button that exists on the page
$('input[type="button"]').each( function(){
// find if the value of the button exists in the array[]
var index = array.indexOf( $(this).attr('value') );
// ternary condition to set the value for 'output'
// if the index is less than zero -- the value does not
// exist in the array; so the string value is empty
// if the index is greater than or equal to zero --
// the value does exist in the array, so the string value
// is the text conversion of the index number
var output = (index < 0 )? '' : index.toString();
// ^not totaly nessecary to convert a number to a string
// in JavaScript/HTML -- it's just a nice coding convention
// to follow.
// update the neighboring span of the button with 'output'
$(this).next('span').html( output );
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<input type='button' value="w">
<span></span>
</div>
<div>
<input type='button' value="x">
<span></span>
</div>
<div>
<input type='button' value="y">
<span></span>
</div>
<div>
<input type='button' value="z">
<span></span>
</div>

Related

Generate Dynamic Inputs with Unique Names in jQuery

In my HTML form, it's possible to add additional inputs dynamically by clicking a button. I've got this part to work, however I need each input to have a unique name.
This is my code so far,
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>
My JS as below,
$(document).ready(function(){
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML(inputNumber) {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number ${inputNumber}</strong></label>\
<input type="text" class="input-medium" name="phone_number[${inputNumber}]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
var x = 1;
$(addButton).on('click', function(e) {
if (x < maxField) {
x++;
$(wrapper).append(fieldHTML(x));
}
if (x >= maxField) {
alert('Limited to 10.');
}
});
$(wrapper).on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
});
});
Using this code, I can get unique name for each input which are created by dynamically. But my problem is name[x] index not works properly when it is removing. That mean, just think I have added 3 input and delete second one and again I am adding new one, then it has same name twice. In this case, it is phone_number[3] for second input and phone_number[3] for thirt one also.
This is the fiddle from above code. Any help is appreciated.
You don't need to index the inputs for PHP either - 3x inputs named phone_number[] will automatically be indexed 0 - 2 on the back end:
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
[phone_number] => Array
(
[0] => a
[1] => b
[2] => c
)
That doesn't help with your plain text Phone Number n label though. And maybe you have your own reasons to want an input name index.
If you think about it, if you're going to allow deletions of any item in the list, and you need the results to be sequential, the only option is to renumber everything each time you make a change. You don't need to include any numbering when you add a new element, just regenerate all numbering.
Here's a working snippet doing that. My changes:
No need to pass the count x to fieldHTML(), we're going to renumber everything after you add the element;
Add a <span class='count'></span> in your label, which we can target and update;
Add a reNumber() function which will iterate over all inputs on the page and number them sequentially;
Call that function after any change;
Notes:
The 2 separate tests if (x < maxField) and if (x >= maxField) can be combined into a single if/else;
If you want to get rid of the duplication of your HTML block, you could give the first one an id like template, and then instead of duplicating that HTML in your JS, just copy the template, eg :
let $copy = $('#template').clone();
wrapper.append($copy);
wrapper and addButton are already jQuery objects, no need to wrap them with $() a second time to use them;
If you do want to number your input names, for consistency the first should probably be phone_number[1];
$(document).ready(function() {
var x = 1;
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML() {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number <span class='count'></span></strong></label>\
<input type="text" class="input-medium" name="phone_number[]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
/**
* Iterate over all inputs and renumber sequentially
*/
function reNumber() {
let count;
wrapper.find('.field_wrapper').each(function (i) {
// .each() index is 0-based, and input #1 is already on the page,
// so extras start at #2
count = i + 2;
$('.count', $(this)).html(count);
// If you want to index your input names, but you can safely leave
// this out, PHP will index them anyway
$('input', $(this)).attr('name', 'phone_number[' + count + ']')
});
}
addButton.on('click', function(e) {
if (x < maxField) {
x++;
wrapper.append(fieldHTML());
reNumber();
} else {
alert('Limited to 10.');
}
});
wrapper.on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
reNumber();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>

Adding And Sorting Array Of Integers from html input field Using Javascript

The Task Is To Get 10 Values Of Array Through HTML Input Field
And Sort Them In Acceding Order
In Order To Append 10 Value's from Html Input field To JS Array I Created one input field which data is passed to array in js and Two labels to keep track of array using innerHTML Method
Note:- The Name Of Array Is itself declared array
whenever a button is hit the input value of html data is appended to js array
using id of input field
and the resulted array is shown to label list every time we insert a new digit to array
and the next label similar to this updates array lenght simantaeneously
to limit it upto 10 index i compare array.lenght with 10th value i.e. 9th index
Note:- Here Array index is starting from 0 so 9th index is 10th digit
But even Though Code Is Not Working Well
Here's my code looks like
HTML File
<div id='div3'>
<input type='number' id='ip1' placeholder='Enter values'></input>
<label id='list' >List values are:</label>
<button id='bt3' onClick='task()'>ADD Value</button>
<label id='len' >Length:</label>
</div>
JS FIle
var array =[];
function task()
{
let pr=array.length;
document.getElementById('len').innerHTML=pr;
if(array.length > 9)
{
let item = document.getElementById('task3val').value;
array.push(item);
document.getElementById('list').innerHTML=array;
}
if(array.length<=9)
{
array.sort(function(a, b){return b - a});
document.getElementById("list").innerHTML = array;
}
}
Please Give Insighful Answer
I have made few changes in your code, It should work for you now. in the above code, you were using the wrong id for the text box
HTML code
<div id='div3'>
<input type='number' id='ip1' placeholder='Enter values'></input>
<br/>
<label id='list' >List values are: </label>
<br/>
<button id='bt3' onClick='task()'>ADD Value</button>
<br/>
<label id='len' >Length:</label>
</div>
Jquery code
<script type="text/javascript">
var array =[];
function task()
{
if(array.length < 3)
{
let item = document.getElementById('ip1').value;
array.push(item);
document.getElementById('list').innerHTML = array.toString();
}
else
{
alert('Only 10 Items allowed!')
}
let pr=array.length;
document.getElementById('len').innerHTML=pr;
}
</script>
If I'm getting it right, this is your solution:
var array =[];
function task()
{
let item = document.getElementById('ip1').value;
array.push(item);
let pr=array.length;
document.getElementById('len').innerHTML= "Length: "+pr;
array.sort(function(a, b){return b - a});
document.getElementById("list").innerHTML = array;
if(pr>=10){
array.pop()
//This removes the last element,
//since it is always ordered, last element is always the smallest
}
}
<div id='div3'>
<input type='number' id='ip1' placeholder='Enter values'></input>
<label id='list' >List values are:</label>
<button id='bt3' onClick='task()'>ADD Value</button>
<label id='len' >Length:</label>
</div>
I am not sure what you were trying to do with that if statement, but the long story is you should first add the item in the array, then the length should return the correct length (so, 1 if there is 1 item, 2 if there are 2, etc.) and then re-order the array and print it on screen
EDIT: I guess you only want to keep the biggest 10 numbers at any time, so what we do is we simply remove the last element once pr reaches 10
this way ?
const
array = []
, arrLimit = 10
, in_ip1 = document.querySelector('#ip1')
, lb_list = document.querySelector('#list')
, bt_bt3 = document.querySelector('#bt3')
, lb_len = document.querySelector('#len')
, errors =
{ noVal : 'please enter a number value'
, outLimit : `Only ${arrLimit} Items allowed!`
, exist : 'value already entered !'
}
bt_bt3.onclick = () =>
{
let inVal = in_ip1.valueAsNumber
if ( isNaN(inVal) ) alert( errors.noVal )
else if (array.length >= arrLimit) alert( errors.outLimit )
else if (array.includes(inVal)) alert( errors.exist )
else
{
array.push( inVal )
array.sort((a,b)=>b-a) //.splice( arrLimit )
lb_list.textContent = array.join(', ')
lb_len.textContent = array.length
}
}
#div3 > * {
display : block;
float : left;
clear : left;
margin : .5em;
}
#list:before { content: 'List values are: ' }
#len:before { content: 'Length: ' }
<div id="div3">
<input id="ip1" type="number" placeholder="Enter values">
<label id="list" ></label>
<button id="bt3">ADD Value</button>
<label id="len" ></label>
</div>

Increment and update value in the total number after insert new rows dynamically

EDIT: I have updated the code with the answers.
I have a increment function that is working fine. However:
1. I would like to set some limits based on the total number available in one of the span. For example, 10. So the incrementing can't be more than 10. #DONE
Another issue is that I am planning to have multiple rows and before I save I want to make sure if we count the increments in every row it should not be more than 10 as well. If it decrease the total number (span) dynamically would be nice.
I'm adding rows dynamically with the ADD button, how can I add news rows that actually work with the current functions? Mine rows just clone the first one and the increment function is disabled.
document.addEventListener('DOMContentLoaded', async function() {
document.querySelector('#addlocationdest').addEventListener('click', add);
});
function add() {
var x = 1;
var container = document.getElementById('destination');
var detail = document.getElementById('row');
var clone = detail.cloneNode(true);
clone.id = "destination" + x;
x++;
container.appendChild(clone);
}
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
for (let row of document.querySelectorAll("form .row")) {
let input = row.querySelector("input");
row.querySelector(".increment").addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
row.querySelector(".decrement").addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
}
});
<div id="location" class="hide">
<div class="title">Transfer details</div><br>
<div class="line padded-s">Total Quantity: <span>10</span></div>
<br>
<form>
<label>New Total Quantity at this location: <span id="totalqty">10</span></label>
<br>
<div id="destination">
<div id="row" class="row">
<button type="button" class="decrement">-</button>
<input type="text" class="quantity" value="0" readonly/>
<button type="button" class="increment">+</button>
<a>Location: </a>
<input type="text" class="location" value="0" readonly/>
</div>
</div>
</form>
<label>Total being transfer: <p id="total-sum"></p></label>
<br>
<button type="button" id="addlocationdest">ADD</button>
<button type="button" id="removelocationdest">REMOVE</button>
</div>
Prologue
As long as the total quantity is fixed at the beginning of the script-execution, this works. Otherwise, it would be best to save the actual allowed total quantity as an attribute, and observe it using a MutationObserver. That way you can update your max. value in your code dynamically, when the total quantity-attribute changes. You can define custom attributes by naming them "data-*" where "*" is a custom name.
Solution for your problem
You are using the same ID on multiple elements. What you meant were classes, so change id="increment" to class="increment", and the same for decrement.
Since we don't want to input something with the buttons, but add listener to them, I'd say it is better to actually use <button>. In forms, buttons act as type="submit", which we don't want, so we need to change it to type="button".
Since the rows and the total quantity actually belong together, it is wiser to place them together into one <form>-element. However, you can still group the buttons and inputs as a row together using <div>.
Now regarding the in-/decrementing of the row's values and the total quantity:
Save the allowed total quantity in a variable
Add event-listener to the corresponding buttons
If action is valid, change row's value
Update total quantity number to totalQuantity - getSumOfRows()
To add new rows dynamically, we create and setup such an element, and append it to the form. See the appendNewRow()-function below.
Sidenote
I have added the readonly attribute to the input-fields so that you cannot enter numbers via keyboard.
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
function updateTotalQuantity() {
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
}
function appendNewRow() {
let row = document.createElement("div");
row.classList.add("row");
let child;
// input.quantity
let input = document.createElement("input");
input.classList.add("quantity");
input.value = "0";
input.setAttribute("readonly", "");
input.setAttribute("type", "text");
row.append(input);
// button.increment
child = document.createElement("button");
child.classList.add("increment");
child.innerHTML = "+";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
updateTotalQuantity();
});
row.append(child);
// button.increment
child = document.createElement("button");
child.classList.add("decrement");
child.innerHTML = "-";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
updateTotalQuantity();
});
row.append(child);
// button.remove-row
child = document.createElement("button");
child.classList.add("remove-row");
child.innerHTML = "Remove";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
row.remove();
updateTotalQuantity();
});
row.append(child);
document.querySelector("form .rows").append(row);
}
document.querySelector("form .add-row").addEventListener("click", () => appendNewRow());
appendNewRow();
});
<form>
<label>Total Quantity: <span id="totalqty">10</span></label>
<br>
<div class="rows">
</div>
<button type="button" class="add-row">Add new row</button>
</form>
QuerySelector only selects the first occurrence so you haven't really added a listener to the second "row". You should use querySelectorAll but, instead of unique ids, use classes.
<input class="increment" type="button" value="+" />
Now you can use document.querySelectorAll(".increment") to get all elements in an array.
You can traverse in the DOM by using parentElement. By knowing which button you clicked, you can traverse up to the form element and then select the first child - which is an input. A more dynamic way would be to use querySelector to select the input, in case the HTML change in the future. Anyway, that's how you can know which input to manipulate based on where the buttons are in the DOM.
I added two global variables, totalSum and maxSum. maxSum is fetched from your span element (which I assigned an unique id to). totalSum makes sure that all inputs combined doesn't exceed maxSum.
You had some duplicate code, so I refactored it into a new method: changeValue.
In all, I think the code speaks for itself.
Oh, this code doesn't take into account that the user can change the value inside the input. I will leave that for you to figure out with an "oninput" listener on each text input.
var totalSum = 0; // 3
var maxSum = 0
var totalSumElement = null;
document.addEventListener('DOMContentLoaded', async function() {
totalSumElement = document.getElementById('total-sum');
maxSum = document.getElementById('max-sum').innerText;
var incrementElements = document.querySelectorAll('.increment'); // 1
var decrementElements = document.querySelectorAll('.decrement');
addListener('click', incrementElements, incrementValue);
addListener('click', decrementElements, decrementValue);
});
function addListener(type, elementArr, func) {
for (element of elementArr) {
element.addEventListener(type, func);
}
}
function withinRange(newValue) {
var maxReached = newValue > maxSum; // 3
var zeroReached = newValue < 0;
return !maxReached && !zeroReached;
}
function changeValue(event, change) { // 4
if (withinRange(totalSum + change)) {
let parent = event.currentTarget.parentElement; // 2
let input = parent.children[0];
let value = parseInt(input.value) || 0;
if (withinRange(value + change)) {
input.value = value + change;
totalSum = totalSum + change;
}
}
totalSumElement.textContent = `Total: ${totalSum}`;
}
function incrementValue(event) {
changeValue(event, 1);
}
function decrementValue(event) {
changeValue(event, -1);
}
#totalqty {
padding-bottom: 1rem;
}
<div id="totalqty" class="line padded-s">Total Quantity: <span id="max-sum">10</span></div>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<p id="total-sum"></p>

Find duplicate values in form elements by class using javascript

I am trying to use javascript to find duplicate values in form elements (input boxes and select drop downs) based on class. This is what I have, but it is not working. Is there a better way to do this? I am new to javascript and saw this as a solution on a different post.
EDIT: Only the inner functions are not called. If I break them out, they get called. Why is this?
<%# taglib prefix="s" uri="/struts-tags"%>
<s:include value="Header.jsp">
<s:param name="pageScript">
<script type="text/javascript">
function checkForDuplicates() {
var hasDuplicates = false;
$('.class_name').each(function () {
var inputsWithSameValue = $(this).val();
hasDuplicates = $('.class_name').not(this).filter(function () {
return $(this).val() === inputsWithSameValue;
}).length > 0;
if (hasDuplicates){
alert("cannot have duplicates")
}
});
}
</script>
</s:param>
</s:include>
<div id="container-content">
<div id="content">
<s:form action="someAction" theme="simple" method="get" id="theForm">
<s:textfield theme="simple" class="class_name"/>
<s:textfield theme="simple" class="class_name" />
<s:select headerKey="" headerValue="Select Value"
list="values" listKey="value" class="class_name" size="1"/>
<s:submit action="" value="Save" onclick="return checkForDuplicates()"/>
</s:form>
<%-- end content --%>
</div>
<%-- end container-content --%>
</div>
<s:include value="Footer.jsp" />
I am importing these:
<script src="scripts/jquery-1.4-min.js"></script>
<script src="scripts/jquery.maskedinput.min.js" type="text/javascript"></script>
<script src="scripts/jquery.supertextarea.min.js" type="text/javascript"></script>
What is the problem? I put a breakpoint inside the first innerfunction after the .each, but it never goes in there.
Thanks
This is based on ROX's answer, however, i think we can check if the next element's input is inside the array without the need of a second function.
function checkDuplicates() {
// get all input elements
var $elems = $('.class_name');
// we store the inputs value inside this array
var values = [];
// return this
var isDuplicated = false;
// loop through elements
$elems.each(function () {
//If value is empty then move to the next iteration.
if(!this.value) return true;
//If the stored array has this value, break from the each method
if(values.indexOf(this.value) !== -1) {
isDuplicated = true;
return false;
}
// store the value
values.push(this.value);
});
return isDuplicated;
}
You might want to check if the input is empty somewhere in your code but that's up to you.
Edit : https://jsfiddle.net/65ss1cxj/
Your could make your function much better, there is no need to loop all over your elements inside your first loop.
Just store your all inputs values into an array, then make that array unique values, and compare the length of them.
// a function to make an array values unique
// http://stackoverflow.com/a/840849/3971911
function eliminateDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
function checkDuplicates() {
// get all input elements
var $elems = $('.class_name');
// we store the inputs value inside this array
var values = [];
// loop through elements
$elems.each(function () {
// if the value is empty, just pass it
if (this.value == '') {
return true;
}
// store the value
values.push(this.value);
});
// make the values array unique
var uniqueValues = eliminateDuplicates(values);
// return false if the unique array length is not equal to the original array
return uniqueValues.length == values.length;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form>
<div><input type="text" class="class_name" /></div>
<div><input type="text" class="class_name" /></div>
<div><input type="text" class="class_name" /></div>
<div><input type="text" class="class_name" /></div>
<input type="submit" value="Go" onclick="return checkDuplicates()" />
</form>

add and get the average of values inputted by user from dynamic textboxes using javascript

I have 5 textboxes displayed, a user can add more textboxes dynamically. However, I am not able to get the the sum and average of the values inputted by users on those textboxes. Can you please help me. I will really appreciate it.
Hear is the HTML code:
<html>
<head>
<title> Grading System </title>
</head>
<script src="addInput.js" language="Javascript" type="text/javascript">
</script>
<body>
<center> GRADING SYSTEM <br><br>
<form method="POST">
<div id="dynamicInput">
Subject number 1<br><input type="text" name="myInputs[]"><br>
Subject number 2<br><input type="text" name="myInputs[]"><br>
Subject number 3<br><input type="text" name="myInputs[]"><br>
Subject number 4<br><input type="text" name="myInputs[]"><br>
Subject number 5<br><input type="text" name="myInputs[]">
</div>
<input type="button" value="Add a subject" onClick="addInput('dynamicInput');">
<input type="button" name="BtnCompute" onClick="avg('dynamicInput');" value="Compute Average">
</form>
</body>
</html>
Then here's the Javascript code:
var counter = 5;
var limit = 10;
var sum=0;
var average=0;
function addInput(divName){
if (counter == limit) {
alert("You have reached the limit of adding " + counter + " inputs");
}
else {
var newdiv = document.createElement('div');
newdiv.innerHTML = "Subject number " + (counter + 1) + " <br><input type='text' name='myInputs[]' >";
document.getElementById(divName).appendChild(newdiv);
counter++
}
}
function avg(divName){
sum += document.getElementsByName("myInputs[]")[0].value;
average = sum / counter
alert("Average is " + average);
return false;
}
The problems with your original function:
function avg(divName) {
// you only look at one <input> element ever time, rather
// than iterating over the collection returned by
// 'document.getElementsByName("myInputs[]")'; plus you're using
// a global value which is (at least potentially) exposed to
// every other function, which makes it vulnerable to being
// over-written by other values; also: you're not ensuring that
// the entered-value is a Number (an <input> returns its value as
// a String, not a Number):
sum += document.getElementsByName("myInputs[]")[0].value;
// here you're working out the average of two global values,
// both of which could be corrupted by other functions, and
// neither of which - being global - are guaranteed to be
// Numbers, since you're not enforcing that anywhere. Also
// you've exposed yourself to potential browser interference
// by not ending the line with the (optional) semi-colon:
average = sum / counter
// this is personal, but for debugging I'd recommend use of
// console.log() (but, again, a purely personal reccomendation
// and not really a 'problem' as such):
alert("Average is " + average);
return false;
}
My correction of your original function would be:
function avg() {
// rather than passing in an argument explicitly, I've opted
// to use a custom data-* attribute to contain the id of the
// relevant <div> (see the HTML below, this simplifies
// changes somewhat in the future):
var div = document.getElementById(this.dataset.divname),
// using Node.querySelectorAll() to retrieve the relevant
// <input> elements (my inclination would be to further
// amend the selector to 'input,' but that depends on
// whether or not you'd have any irrelevant <input>
// elements contained within the same <div>):
inputs = div.querySelectorAll('input[type=text][name="myInputs[]"]'),
// using Function.prototype.call() to apply the
// Array.prototoype.map() function to the Array-like
// NodeList returned by document.querySelectorAll(),
// and returns an Array of (in this case) entered values:
sum = Array.prototype.map.call(inputs, function (inputNode) {
// here we convert the existing value of the <input>
// element-node to a Number (<input> elements return
// their values as a String) or, if the value can't
// be converted to a Number, or the <input> has no
// entered value, we return 0:
return parseFloat(inputNode.value) || 0;
// using Array.prototype.reduce() to sum the
// Array of values returned by Array.prototype.map():
}).reduce(function (a, b) {
// here we return the sum of the previous number
// and the current number:
return a + b;
// the 0 here is the initial starting value
// to which the numbers are added:
}, 0),
// ensuring that the counter variable (first
// argument) is parsed into an integer value
// (using parseInt(), in base-10 (the second
// argument):
average = sum / parseInt(counter, 10);
console.log("Average is " + average);
return false;
}
// here we use JavaScript to attach the event-handling function to
// the relevant <input> (I added the 'id' to enable this), and
// this binds the avg() function to handle the 'click' event:
document.getElementById('btnCompute').addEventListener('click', avg);
The HTML for the amended <input> element:
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
var counter = 5;
var limit = 10;
function addInput() {
// note that I've amended this function also, to
// use the 'data-divname' attribute to hold the
// 'id' of the relevant <div>:
var div = document.getElementById(this.dataset.divname);
if (counter == limit) {
alert("You have reached the limit of adding " + counter + " inputs");
} else {
var newdiv = document.createElement('div');
newdiv.innerHTML = "Subject number " + (counter + 1) + " <br><input type='text' name='myInputs[]' >";
div.appendChild(newdiv);
counter++
}
}
function avg() {
var div = document.getElementById(this.dataset.divname),
inputs = div.querySelectorAll('input[type=text][name="myInputs[]"]'),
sum = Array.prototype.map.call(inputs, function(inputNode) {
return parseFloat(inputNode.value) || 0;
}).reduce(function(a, b) {
return a + b;
}, 0),
average = sum / parseInt(counter, 10);
snippet.log("Average is " + average);
return false;
}
document.getElementById('addNew').addEventListener('click', addInput);
document.getElementById('btnCompute').addEventListener('click', avg);
label,
input[type=text] {
display: block;
}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<!--
Note that I've also amended your HTML, removing the
unnecessary <br /> elements, instead using CSS to
provide line-breaks; and also wrapping the <input>
elements in <labels>, both to enable the CSS line-
breaks and to enable clicks on the text to focus
the associated (nested) <input> element:
-->
<form method="POST" action="#">
<div id="dynamicInput">
<label>Subject number 1
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 2
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 3
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 4
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 5
<input type="text" name="myInputs[]" />
</label>
</div>
<input id="addNew" data-divname="dynamicInput" type="button" value="Add a subject" />
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
External JS Fiddle demo, for experimentation/development.
Despite my correction of your function though, I'd probably take an alternative approach:
// because I retrieve the number of elements in a couple
// of places, I created a simple function to retrieve
// that number of elements:
function currentlyExisting(selector) {
return document.querySelectorAll(selector).length;
}
// rewritten in my own style (though this is irrelevant to the
// the question you asked):
function addNew() {
// still using the value of the custom 'data-divname'
// attribute:
var parent = document.getElementById(this.dataset.divname),
// rather than creating a HTML string, here I create nodes
// using document.createElement() and a textNode, using
// document.createTextNode():
label = document.createElement('label'),
input = document.createElement('input'),
// getting the number of currently-existing <input>
// elements using the above function, passing the
// selector as an argument:
current = currentlyExisting('input[name="myInputs[]"'),
limit = 10;
// if the current number of <input> elements is less than
// the limit:
if (current < limit) {
// we set the type of the created <input>:
input.type = 'text';
// and the name property:
input.name = 'myInputs[]';
// appending a textNode to the created <label> element:
label.appendChild(document.createTextNode('Subject number ' + (current + 1) + ':' ));
// appending the created <input> to the created <label>:
label.appendChild(input);
// attaching the created <label>, along with its own
// childNodes, to the parent div (retrieved and cached above):
parent.appendChild(label);
// setting the disabled property to true if the updated
// number of <input> elements is equal to, or greater than,
// the limit; or to false if the number of <input> elements
// remains less than the limit (preventing the addition of
// more <input> elements than that identified by the limit):
this.disabled = currentlyExisting('input[name="myInputs[]"') >= limit;
}
// all functions return a value, whether it's explicitly defined
// or undefined (as this one will), the return false of your
// original function can be added here instead if you prefer,
// but I - personally - feel it's unnecessary, so I left it out.
}
function average() {
// retrieving the relevant <div> element using the
// data-divname attribute once again:
var parent = document.getElementById(this.dataset.divname),
// retrieving the relevant <input> elements:
inputs = parent.querySelectorAll('input[name="myInputs[]"]'),
// creating an Array of the values of the relevant
// <input> elements, using Function.prototype.call()
// in order to use Array.prototype.map() on the
// Array-like NodeList returned by querySelectorAll():
values = Array.prototype.map.call(inputs, function (input) {
// returning the value of the current <input>
// element as a number to the array, using
// parseFloat() to convert that String to a
// Number; or returning 0 if the String cannot
// be parsed as a Number:
return parseFloat(input.value) || 0;
// using Array.prototype.reduce() to reduce the Array
// of numeric values (provided by map()) to a single
// number, the sum of the values:
}).reduce(function (a, b) {
// adding the previous and current values
// together:
return a + b;
// here the 0 is the initial value before the Array
// 'reduction' takes place:
}, 0),
average = sum / inputs.length;
// adding the values to the appropriate elements on screen
// for easier visualisation (and in a manner that persists):
document.getElementById('average').textContent = average;
document.getElementById('sum').textContent = sum;
document.getElementById('total').textContent = inputs.length;
}
// adding the click event handlers to the relevant button <input>
// elements using EventTarget.addEventListener():
document.getElementById('addNew').addEventListener('click', addNew);
document.getElementById('btnCompute').addEventListener('click', average);
function currentlyExisting(selector) {
return document.querySelectorAll(selector).length;
}
function addNew() {
var parent = document.getElementById(this.dataset.divname),
label = document.createElement('label'),
input = document.createElement('input'),
current = currentlyExisting('input[name="myInputs[]"'),
limit = 10;
if (current < limit) {
input.type = 'text';
input.name = 'myInputs[]';
label.appendChild(document.createTextNode('Subject number ' + (current + 1) + ':'));
label.appendChild(input);
parent.appendChild(label);
this.disabled = currentlyExisting('input[name="myInputs[]"') >= limit;
}
}
function average() {
var parent = document.getElementById('dynamicInput'),
inputs = parent.querySelectorAll('input[name="myInputs[]"]'),
sum = Array.prototype.map.call(inputs, function(input) {
return parseFloat(input.value) || 0;
}).reduce(function(a, b) {
return a + b;
}, 0),
average = sum / inputs.length;
document.getElementById('average').textContent = average;
document.getElementById('sum').textContent = sum;
document.getElementById('total').textContent = inputs.length;
}
document.getElementById('addNew').addEventListener('click', addNew);
document.getElementById('btnCompute').addEventListener('click', average);
label,
input[type=text] {
display: block;
}
#average::before {
content: 'Average: ';
}
#sum::before {
content: ', sum: ';
}
#total::before {
content: ', of: ';
}
#total::after {
content: ' entries.'
}
#total:empty::before,
#total:empty::after,
#sum:empty::before,
#average:empty::before {
content: '';
display: none;
}
<div id="results">
<span id="average"></span>
<span id="sum"></span>
<span id="total"></span>
</div>
<form method="POST" action="#">
<div id="dynamicInput">
<label>Subject number 1:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 2:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 3:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 4:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 5:
<input type="text" name="myInputs[]" />
</label>
</div>
<input id="addNew" data-divname="dynamicInput" type="button" value="Add a subject" />
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
</form>
External JS Fiddle demo, for experimentation/development.
References
HTML:
Custom data-* attributes.
JavaScript:
Array.prototype.map().
Array.prototype.reduce().
document.getElementById().
document.getElementsByName().
document.querySelectorAll().
EventTarget.addEventListener().
Function.prototype.call().
HTMLElement.dataset.
parseFloat().
parseInt().
So I think you're missing a few things.
sum += document.getElementsByName("myInputs[]")[0].value;
this line gets only the first input field value, so you need a loop through all the input fields to get the actual sum
for (var i = 0; i < counter; i++)
sum += parseInt(document.getElementsByName("myInputs[]")[i].value);
Next, notice the addition of the parseInt(); method. This converts the input value (which javascript sees as a string by default) into an integer in which you can perform calculations on.
Hope that helps you, cheers

Categories

Resources