Issues using an array created by getElementsbyClassName in a for loop - javascript

I am trying to call a set of elements from an HTML doc by class name, which I understand returns them as an array-like object. I would like to cycle through the values of this array in a for loop, but the loop is returning 'undefined' for every value in the array, even though there is definitely a value in the elements that I am calling.
I basically want to output the following onto the web page, into some simple div elements:
Age prediction line 1: Hello George, you are 28 in 2019
Age prediction line 2: Hello Andy, you are 29 in 2020
Age prediction line 3: Hello Kyle, you are 30 in 2021
Age prediction line 4: Hello Jenny, you are 31 in 2022
The names are the values of the elements in the "myinput" class. I am using a pretty standard for loop as below:
const years = [2019, 2020, 2021, 2022];
var name = document.getElementsByClassName("myinput");
var age = document.getElementById("agefield").value;
var intage = parseInt(age, 10);
for (i = 0; i < 4; i++) {
document.getElementsByClassName("ageprediction")[i].innerHTML = "Age
prediction line " + (i+1) + ": ";
document.getElementsByClassName("testdiv")[i].innerHTML = " Hello " +
name[i].value + " you are " + [intage+i] + " in " + years[i];
}
There is just one agefield element, and calling that by Id, adding.value and converting to an integer works fine. But after understanding and reading everywhere that you could use code like this to cycle through the elements of a getElementbyClass array in a for loop using the current index value, I am still getting 'undefined' as the result. Is this because name[i].value is not the correct code?
UPDATE: A couple of people asked to see the relevant HTML as well, here it is:
<section id="form">
<input class="myinput" value="George"/><br/><br/>
<input class="myinput" value="Andy"/><br/><br/>
<input class="myinput" value="Kyle"/><br/><br/>
<input class="myinput" value="Jenny"/><br/><br/>
<input id="agefield" placeholder="Your age" value="28"/><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
</section>
As you can see, I'm just using predefined values in the inputs for now for testing purposes.

It is actually works. Check out this link : https://jsfiddle.net/dy0feLha
const years = [2019, 2020, 2021, 2022];
var name = document.getElementsByClassName("myinput");
var age = document.getElementById("agefield").value;
var intage = parseInt(age, 10);
for (i = 0; i < 4; i++) {
document.getElementsByClassName("ageprediction")[i].innerHTML = "Age prediction line " + (i+1) + ": ";
document.getElementsByClassName("testdiv")[i].innerHTML = " Hello " +
name[i].value + " you are " + [intage+i] + " in " + years[i];
}
I simply just changed first line in the loop. It has to be in the same line. But it maybe already same in your code editor.

You can't affect content to a variable named "name", the content will be automatically converted to string :
window.name will convert all values to their string representations by
using the toString method.
From developer.mozilla
It works if you simply rename the variable
const years = [2019, 2020, 2021, 2022];
var names = document.getElementsByClassName("myinput");
console.log(names);
var age = document.getElementById("agefield").value;
var intage = parseInt(age, 10);
for (i = 0; i < 4; i++) {
document.getElementsByClassName("ageprediction")[i].innerHTML = "Age prediction line " + (i+1) + ": ";
document.getElementsByClassName("testdiv")[i].innerHTML = " Hello " + names[i].value + " you are " + [intage+i] + " in " + years[i];
}
<section id="form">
<input class="myinput" value="George"/><br/><br/>
<input class="myinput" value="Andy"/><br/><br/>
<input class="myinput" value="Kyle"/><br/><br/>
<input class="myinput" value="Jenny"/><br/><br/>
<input id="agefield" placeholder="Your age" value="28"/><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
<div class ="ageprediction"></div><div class = "testdiv">Testing</div><br/><br/>
</section>

Related

Dynamically Generating HTML

I'm an absolute beginner, been self-studying for two weeks in preparation for a bootcamp coming up in a month. In an effort to improve my skills and "learn to think like a developer", I've taken on a project which attempts to solve a real-world problem.
My wife runs a business requiring her to generate invoices for her clients. I am attempting to use HTML and JavaScript to build a web-based application which enables her to quickly create custom invoices instead of writing them out manually each time.
In the current version, a prompt requests a number from her. This number generates an equal number of three-column rows in HTML. This enables her to customize the invoice with the exact number of fields she needs.
Column 1 contains the product name. In the same row, column 2 contains the unit count, while column 3 contains the total cost of that product, which is the base price multiplied by the unit count. I want this calculation to occur upon a button click. But, I'm stuck.
In order to perform the math, I need to be able to get the integers from each row and column and pass them into a function. But because each row and column was generated automatically, they have no unique attributes and cannot thereby be identified. I am able to perform the math down each column, but not across each row.
As I'm so new, having only learned from a couple introductory Codecademy courses and some YouTube videos, I don't know how to evaluate whether I'm approaching the project entirely wrong, or if there's some trick I'm missing, or if there's just something haven't learned yet. If someone with some expertise could nudge me in the right direction, I'd really appreciate it!
I've attached all the code to this post. Apologies if it's a horrible mess. Go easy on me, I'm a beginner!
const invDate = Date();
var field = "<label for='Item'>Item:</label> <input type=text name='item'>" +
"<label for='qty'> Qty: </label> <input type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input type='number' name='price'> <br><br>";
document.getElementById('newInvoice').onclick = function(){
let invoicedName = prompt('Who is this invoice made out to?', 'Enter a name');
let productFields = Number(prompt('How many product names on this invoice?', 'Please enter a number'));
let fields = ''
let dynHtml = ''
if (invoicedName != null && productFields != null) {
for (let i = 1; i <= productFields; i++) {
fields += field };
} else { alert('Please enter valid inputs.');
};
dynHtml = "<center><img src ='logo.jpg'><br></br>" +
"<h1>INVOICE</h1>" +
"<p>Prepared for: <b>" + invoicedName + "</b>, on" +
"<p>" + invDate + "</p><br></br>" +
fields + "<br></br>" +
"<button id ='calculate'>Calculate</button></center>";
document.write(dynHtml);
document.getElementById('calculate').onclick = function getQtyFields() {
let qtyInputs = document.getElementsByName('qty'),
resultQty = 0;
for ( let j = 0; j < qtyInputs.length; j++ ) {
if ( qtyInputs[j].id.indexOf('qty') == 0 ) {
let num = parseFloat( qtyInputs[j].value );
if( !isNaN(num) ) resultQty += num;
}
}
let priceInputs = document.getElementsByName('price'),
resultPrice = 0;
for( let k = 0; k < priceInputs.length; k++ ) {
if( priceInputs[k].id.indexOf('price') == 0 ) {
let num = parseFloat( priceInputs[k].value );
if( !isNaN(num) ) resultPrice += num;
}
}
alert(resultQty); alert(resultPrice)
}
}
Here's Screenshot,
As you said, your main issue is finding a way to give every input field within their own row their own unique identifier. This way you can calaulate the price for each row and insert it into the price field.
You first have to start with the fields:
var field = "<label for='Item'>Item:</label> <input type=text name='item'>" +
"<label for='qty'> Qty: </label> <input type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input type='number' name='price'> <br><br>";
document.getElementById('newInvoice').onclick = function(){
...
for (let i = 1; i <= productFields; i++) {
fields += field };
...
};
Every group needs its own identifier. That way you can later refer to every input in each row to calculate the sub total price. The class attribute is something you can assign to multiple elements to refer to them later. This class can be anything as long as it doesn't conflict with the class for any other row. You can use the i of the loop as your identifier, since it changes with every loop.
for (let i = 1; i <= productFields; i++) {
var field = "<label for='Item'>Item:</label> <input class='row-" + i + "' type=text name='item'>" +
"<label for='qty'> Qty: </label> <input class='row-" + i + "' type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input class='row-" + i + "' type='number' name='price'> <br><br>";
fields += field
};
This will add the class row-{i} to every field in every row. Better yet, you can refactor this into its own function
function generateFields(i) {
return "<label for='Item'>Item:</label> <input class='row-" + i + "' type=text name='item'>" +
"<label for='qty'> Qty: </label> <input class='row-" + i + "' type='number' name='qty'>" +
"<label for='Price'> Price ($):</label> <input class='row-" + i + "' type='number' name='price'> <br><br>";
}
for (let i = 1; i <= productFields; i++) {
fields += generateFields(i);
};
You get something similar to the following html
<label for='Item'>Item:</label> <input type=text class='row-1' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-1' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-1' name='price'> <br><br>
<label for='Item'>Item:</label> <input type=text class='row-2' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-2' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-2' name='price'> <br><br>
<label for='Item'>Item:</label> <input type=text class='row-3' name='item'>
<label for='qty'> Qty: </label> <input type='number' class='row-3' name='qty'>
<label for='Price'> Price ($):</label> <input type='number' class='row-3' name='price'> <br><br>
Now in your calculate function, you can refer to these rows and calculate their price. Here you can loop over the 'item' input fields.
document.getElementById('calculate').onclick = function getQtyFields() {
let itemInputs= document.getElementsByName('item')
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier);
}
...
}
row is now an HTMLCollection which contains the item, qty, and price for each row. With HTMLCollections you can select any of its elements by its name using .namedItem(<name>)
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier); // ex. 'row-1'
const qty = row.namedItem('qty').value;
const basePrice = [YOUR BASE PRICE];
const itemSubTotal = basePrice * qty; // heres is your item sub total!
}
You can now insert the price price into the input field for that row.
for(let i = 0; i < itemInputs.length; i++){
const identifier = itemInputs[i].className // get the class name of every item ex. 'row-1'
const row = document.getElementsByClassName(identifier); // ex. 'row-1'
const qty = row.namedItem('qty').value;
const basePrice = [YOUR BASE PRICE];
const itemSubTotal = basePrice * qty; // heres is your item sub total!
const price = row.namedItem('price');
price.value = itemSubTotal; // insert subtotal inside the price field
}
Now once you click on calculate, you should see the alerts for total price and total quantity and should see the price input fields filled with the calculated price.
This solution is very basic and shouldn't introduce any new concepts you don't know. Later on in your bootcamp, you will learn will about design patterns and frameworks that can make these kind of projects easier to manage and scale.
Hit me up if run into any problems or have any other questions!

Unable to generate a multiplication table with user input in JavaScript

I have a page which prompts the user to enter a positive integer from 1 to 9, then the javascript code will generate a multiplication table from the input value all the way to 9. I am getting an error in which I cannot retrieve the value and do a multiplication with it.
function timesTable()
{
var values = document.getElementById('value1');
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "\n";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
Expected result:
You have to take the value of the element not the element itself
var values = document.getElementById('value1').value;
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "<br>";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
You are trying to multiply the element itself. What you actually want is the value.
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<9; i++) {
showTables += values + " x " + i +" = "+ values*i + "\n";
}
var p_tables = document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="text" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>
the javascript line in which you are trying to find value, is wrong as it will return the whole DOM and it's attributes and property.
You just have to find it's value, replace you line
var values = document.getElementById('value1');
with
var values = document.getElementById('value1').value;
This does what you want.
Note that if the user enters something unexpected, it may still fail. You can use an input of type="number" to require an integer (at least in some browsers.)
const userValue = document.getElementById("value1").value;
const p_tables = document.getElementById("tables");
let outputHtml = "";
for(let i = 1; i < 10; i++){
outputHtml += userValue + " x " + i + " = " + userValue * i + "<br/>";
}
p_tables.innerHTML = outputHtml;
you are using input field as text for table generation its better to use Number as input type and to get the value of input field you have to use value function as used in above code and for line break use
<\br>(please ignore '\').
function timesTable()
{
var values = document.getElementById('value1').value;
var showTables = '';
for (var i=1; i<=9; i++) {
showTables += values + " x " + i +" = "+ values*i + "<br>";
}
document.getElementById('tables').innerHTML = showTables;
}
<label>Enter an integer from 1 to 9 : </label>
<input type="Number" size=20 id=value1 name="value">
<button onclick="timesTable()">Generate times table</button><br> <br>
<p id="tables"></p>

JavaScript don't read the text in multiselect

I want to capture the values of a form to elaborate them in some function before to record them in the DB. I think I should use JavaScript so here we are:
My model:
class Region(models.Model):
number = models.CommaSeparatedIntegerField(max_length=9, unique=True)
city = models.OneToOneField(Community, blank= True, null=True)
resources = models.ManyToManyField(Resource, blank=True)
My form:
class Crea_RegionForm(forms.ModelForm):
quantity = forms.IntegerField(initial=1)
resources=MyModelMultipleChoiceField(Resource.objects.all(),
widget=forms.CheckboxSelectMultiple, label='Risorse')
class Meta:
model = Region
fields = ('number', 'city', 'resources')
def __init__(self, *args, **kwargs):
super(Crea_RegionForm, self).__init__(*args, **kwargs)
quantity = self.fields['quantity'] #costum field
My MyModelMultipleChoiceField:
class MyModelMultipleChoiceField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return format_html(
'<span style="font-weight:normal">{}</span>', obj.name)
So the html for resources in my page:
Risorse
<ul id="id_resources">
<li>
<label for="id_resources_0">
<input id="id_resources_0" name="resources" type="checkbox" value="1" />
<span style="font-weight:normal">Turism</span>
</label>
</li>
<li>
<label for="id_resources_1">
<input id="id_resources_1" name="resources" type="checkbox" value="2" />
<span style="font-weight:normal">Agricole</span>
</label>
</li>
<li>
#others
</li>
</ul>
and finally my javascript in crea_region.html:
<input type="button" value="Create" id="CreateButton" />
<script type="text/javascript">
<!--
var myform = document.getElementById("crea_region_form");
var number = myform.number.value;
var city_index = document.getElementById("id_city").selectedIndex;
var city;
if (city_index > -1) {
city = document.getElementById("id_city").options[city_index].text;
document.write("city: " + city);
}
var risorse = myform.resources;
var selectedresources = [];
for (var i = 0; i < risorse.length; i++) {
document.write("risorse: " + risorse[i].text);
//document.write("risorse: " + risorse[i].value);
if (risorse[i].selected) selectedresources.push(risorse[i].value);
}
document.write("risorse: " + selectedresources);
var quantity = myform.quantity.value;
var button = document.getElementById("CreateButton");
// to be completed
//-->
</script>
The problem is in resources: a costum ModelMultipleChoiceField that create a list of checkbox. My script can't read the textual and the lines
document.write("risorse: " + risorse[i].text) give=> risorse: undefined
document.write("risorse: " + risorse[i].value) give=> risorse: 1
document.write("risorse: " + selectedresources) give=> risorse:
EDIT
My new script:
...
var risorse = myform.resources;
document.write("risorse: " + risorse[1].nextSibling.innerHTML)
console.log(risorse)
console.log(risorse[1].nextSibling.innerHTML)
var selectedresources = [];
for (var i = 0; i < risorse.length; i++) {
document.write("risorse: " + risorse[i].nextSibling.innerHTML);
if (risorse[i].selected) selectedresources.push(risorse[i].nextSibling.innerHTML);
}
document.write("risorse: " + selectedresources);
document.write("risorse: " + risorse[1].nextSibling.innerHTML) give=> undefined
console.log(risorse) give => RadioNodeList [ <input#id_resources_0>, <input#id_resources_1>, <input#id_resources_2>, <input#id_resources_3>, <input#id_resources_4>, <input#id_resources_5>, <input#id_resources_6>, <input#id_resources_7> ]
console.log(risorse[1].nextSibling.innerHTML) give=> undefined
Sorry, but checkbox doesn't have a text attribute, according to this link, the only attributes are: checked, name, required, value.
You will need to update your code, probably have an object somewhere in your app with key=>text so you can find the text that correspond to specific key, something like this:
var _texts = {'1': 'Text 1', '2': 'Text 2'};
var risorse = myform.resources;
for (var i = 0; i < risorse.length; i++) {
document.write("risorse: " + _texts[risorse[i].value]);
}
Fill free to validate if risorse[i].value exist in texts, you can use the hasOwnProperty function.
Or, you can take advantage of you current html and use the nextSibling:
var risorse = myform.resources;
for (var i = 0; i < risorse.length; i++) {
document.write("risorse: " + risorse[i].nextSibling.innerHTML);
}
Done!! This look working...
My script:
...
var risorse = myform.resources;
var selectedresources = [];
for (var i = 0; i < risorse.length; i++) {
if (risorse[i].checked) selectedresources.push(risorse[i].nextSibling.nextSibling.innerHTML);
}
document.write("risorse: " + selectedresources);

Make a html unordered list from javascript array

I'm having a bit of a problem. I'm trying to create a unordered list from a javascript array, here is my code:
var names = [];
var nameList = "";
function submit()
{
var name = document.getElementById("enter");
var theName = name.value;
names.push(theName);
nameList += "<li>" + names + "</li>";
document.getElementById("name").innerHTML = nameList;
}
<input id="enter" type="text">
<input type="button" value="Enter name" onclick="submit()">
<br>
<br>
<div id="name"></div>
For example, if I post 2 names, Name1 and Name2 my list looks like this:
•Name1
•Name1,Name2
I want it to look like this:
•Name1
•Name2
If you look at your code, you are only creating one li with all your names as the content. What you want to do is loop over your names and create a separate li for each, right?
Change:
nameList += "<li>" + names + "</li>";
to:
nameList = "";
for (var i = 0, name; name = names[i]; i++) {
nameList += "<li>" + name + "</li>";
}
If you are interested in some better practices, you can check out a rewrite of your logic here: http://jsfiddle.net/rgthree/ccyo77ep/
function submit()
{
var name = document.getElementById("enter");
var theName = name.value;
names.push(theName);
document.getElementById("name").innerHTML = "";
for (var I = 0; I < names.length; I++)
{
nameList = "<li>" + names[I] + "</li>";
document.getElementById("name").innerHTML += nameList;
}
}
You are using an array, when you print an array JavaScript will show all the entries of the array separated by commas. You need to iterate over the array to make it work. However you can optimize this:
var names = [];
function displayUserName()
{
var theName = document.getElementById("enter").value;
if (theName == "" || theName.length == 0)
{
return false; //stop the function since the value is empty.
}
names.push(theName);
document.getElementById("name").children[0].innerHTML += "<li>"+names[names.length-1]+"</li>";
}
<input id="enter" type="text">
<input type="button" value="Enter name" onclick="displayUserName()">
<br>
<br>
<div id="name"><ul></ul></div>
In this example the HTML is syntactically correct by using the UL (or unordered list) container to which the lis (list items) are added.
document.getElementById("name").children[0].innerHTML += "<li>"+names[names.length-1]+"</li>";
This line selects the div with the name: name and its first child (the ul). It then appends the LI to the list.
As #FelixKling said: avoid using reserved or ambiguous names.
<div>
<label for="new-product">Add Product</label><br /><br /><input id="new-product" type="text"><br /><br /><button>Add</button>
</div>
<div>
<ul id="products">
</ul>
<p id="count"></p>
</div>
var products = [];
var productInput = document.getElementById("new-product");
var addButton = document.getElementsByTagName("button")[0];
var productListHtml = "";
var abc = 0;
addButton.addEventListener("click", addProduct);
function addProduct() {
products.push(productInput.value);
productList();
}
function productList() {
productListHtml += "<li>" + products[abc] + "</li>";
document.getElementById("products").innerHTML = productListHtml;
abc++;
}

How do I define a variable and call it in a hidden input field?

I have a javascript code that does some math and I need to be able to just pull the final result and include it in a form. To do that I wanted to just use a <input type="hidden" name="new total">. I want to grab the results from the field "New Total". You can see my javascript code in action here. http://jsfiddle.net/danielrbuchanan/yjrTZ/5/
var prices = [];
function remove(arr,itm){
var indx = arr.indexOf(itm);
if (indx !== -1){
arr.splice(indx,1);
}
}
function calculateSectedDues(checkbox, amount) {
if (checkbox.checked === true) {
prices.push(amount);
} else {
remove(prices, amount);
}
var total = 0;
for (var i = 0, len = prices.length; i < len; i++)
total += prices[i];
var min = prices.slice().sort(function(a,b){return a-b})[0];
if(typeof min === 'undefined') min = 0;
var withDiscount = total - min;
var discountAmount = withDiscount - total;
//document.grad_enroll_form.total.value = total;
document.querySelector("#value").innerHTML = "Total: $"+total+'<br>';
document.querySelector("#value").innerHTML += "Discount: $"+discountAmount+'<br>';
document.querySelector("#value").innerHTML += "New total: $"+withDiscount+'<br>';
}
It seems simple, for some reason I'm drawing a blank on how to do it.
What i think you want is to set an input value with your variable, so i think it should go like this:
Javascript:
document.getElementById("newTotal").value = your_new_total;
HTML:
<form>
<input id='newTotal' type='hidden' name='NewTotal' value='' />
</form>
Hope this helps.
<input type="hidden" id="new_total" name="new_total">
<!-- no spaces in name and id -->
Put this code at end of the calculateSectedDues function
document.getElementById('new_total').value = withDiscount
You have a couple of options.
Option 1:
jQuery
Add a visible div, how ever you want to do things.
Bind onChange() events to your input fields that drive the calculation.
<div id="finalValue"></div>
<script language="javascript">
$(function() {
$("form input").change(function() {
calculateSectedDues($("#checkboxid"), $("#amountid").val());
});
});
</script>
In the calculateSectedDues() function add this to the end:
$("#finalValue").html("Total: $" + total + "<br />Discount: " + discountAmount + "<br />New Total: " + withDiscount + "<br />");
Option 2:
No jQuery
<div id="finalValue"></div>
<script language="javascript">
function bindEvent() {
calculateSectedDues(document.getElementById("checkboxid"), document.getElementById("amountid").value);
}
window.onload = function()
{
var elJ = document.getElementsByTagName("input");
for (var i=0;i<elJ.length;i++) {
elJ[i].addEventListener('change',bindEvent,false);
}
}
</script>
In the calculateSectedDues() function add this to the end:
document.getElementById("finalValue").InnerHtml("Total: $" + total + "<br />Discount: " + discountAmount + "<br />New Total: " + withDiscount + "<br />");
Is that what you are trying to get after?
You should also remember to do the calculation on the Server Side too and not rely on the JavaScript to do it for you. This would simply be to notify the user what they are getting before hand. Otherwise, front-end hacks could easily be done if you are using a hidden total field.

Categories

Resources