Quantity Undefined, Totals Not Calculating - javascript

I'm working on a program that lets you add items to the shopping cart, change the quantity of a certain item in the cart, and computes the subtotal, taxes, shipping cost, and total dynamically.
Right now I'm running into the problem where all of my totals computed are 0.
Here is where I believe I most likely made an error, I'll provide the whole code at the bottom of the page.
//Used to dynamically change subtotal,tax,shipping, and total when quantity is adjusted
$("#selected-list").on('change', '.quantity', calculateTotalPrice);
function calculateTotalPrice(){
var subtotal = calculateSubtotal();
var tax = calculateTax();
var shipping = calculateShipping();
var total = calculateTotal();
$('#subtotal').html(subtotal.toFixed(2));
$('#tax').html(tax.toFixed(2));
$('#shipping').html(shipping.toFixed(2));
$('#total').html(total.toFixed(2));
}
function calculateSubtotal(){
var subtotal = 0;
var quantity = 1;
for(var i = 0; i < cart.lengh; i++){
quantity = parseInt($(this).val());
var productPrice = parseInt(item_list[cart[i]].price);
subtotal += parseInt(productPrice * quantity);
}
return subtotal;
}
function calculateTax(){
var taxAmount = 0;
var taxes = .06;
var subtotal = calculateSubtotal();
taxAmount = parseInt(subtotal * taxes);
return taxAmount;
}
function calculateShipping(){
var shippingAmount = 0;
var shippingPerc = .02;
var subtotal = calculateSubtotal();
shippingAmount = parseInt(subtotal * shippingPerc);
return shippingAmount;
}
function calculateTotal(){
var totalCost = 0;
var subtotal = calculateSubtotal();
var taxes = calculateTax();
var shippingCost = calculateShipping();
totalCost = parseInt(subtotal + taxes + shippingCost);
return totalCost;
}
I've been messing around with it for a while, and what's interesting is if I put an alert right after my for loop in calculateSubtotal() this way:
alert(subtotal)
alert(item_list[cart[0]].price)
The subtotal will always print to be 0, and the item_list[cart[0]].price will always print out the correct price of the item in the cart. This confuses me because I made the default value of quantity to be 1, and if it's getting the price correctly then it shouldn't even be able to equal 0.
Here is the rest of my code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html>
<head>
<meta charset="utf-8">
<!-- Set the viewport so this responsive site displays correctly on mobile devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>UWW Semester Planner </title>
<!-- Include bootstrap CSS -->
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"> </script>
<style type="text/css">
.courselist { cursor: pointer;}
.t-head { color: #fff; background-color: #333366; font-size: 20px; line- height: 20px; }
#top-box, #footer { background-color: #330066; color: #fff; text-align: center;}
#content {border: 1px solid #330099;}
</style>
<script>
$(function(){
/* The following function defines a constructor for creating an array of objects with four properties.
The keyword "this" refers to the instance of the object
*/
function Item(type,title,description,price){
this.type = type;
this.title = title;
this.description = description;
this.price = price;
}
//create an array to store items
var item_list = [];
var cart = []; //store index of elements in the shopping cart
//add items
//add baseballgear items
item_list.push(new Item('baseballgear','Louisville Slugger', 'The finest craftsmanship and finest materials ensure stepping to the plate with the Louisville Slugger® M9 Maple M110 Bat will allow you to swing what the pros swing.', 79.99 ));
item_list.push(new Item('baseballgear','Marucci Bat', 'Named for one of the most lethal hitters in the game, the Marucci® CUTCH22 Pro Model Baseball Bat features the same cut and finish swung by MLB® center fielder, Andrew McCutchen. The "Cutch" features a large, powerful, balanced barrel with a sleek cherry red and grey finish to deliver maximum performance at the plate. This adult wooden bat is also handcrafted and bone-rubbed to ensure superior quality and surface hardness.', 139.99));
item_list.push(new Item('baseballgear', 'Rawlings Glove', "Unrivaled quality season after season, the Rawlings® 11.25'' Pro Preferred® Series Glove returns to provide elite craftsmanship and superior performance for elite middle infielders.",349.99));
item_list.push(new Item('baseballgear', 'Wilson Glove', "Enhance your field performance with unrivaled dependability with the Wilson® 11.5 A2000™ Series Glove. Made with Pro Stock® leather for long-lasting performance, this glove's construction is preferred by professionals for its top-notch quality. Dri-Lex® technology in the wrist lining transfers moisture away from the skin to keep you cool and dry. The advanced design has been improved upon by the Wilson&Reg; Advisory Staff.",249.99 ));
item_list.push(new Item('baseballgear', 'Easton Baseball Helmet', 'Give your favorite player maximum protection at the plate with the Easton® Junior Z5 Elite Baseball Helmet. The ABS shell withstands impact and disperses energy away from the head, with a stylish Digi-Camo design. Featuring dual density foam liner for advanced comfort, this helmet boasts BioDri™ padded inner liner to wick moisture away from the skin to keep them cool and dry. Wrapped ear pads provide enhanced coverage around the head.', 54.99));
item_list.push(new Item('baseballgear', 'Rawlings Batting Gloves', 'Get the most out of your batting gloves this season with the Rawlings® Adult Workhorse 950 Batting Gloves. These gloves feature an Oiltac® leather palm pad to provide better grip and softness. Equipped with a Dura-Plus™ pad for added protection in the palm, the Dynamic Fit System™ provides greater comfort, flex, and feel during every play. The adjustable wrist closure is reinforced to provide a more secure fit', 34.99));
//add soccergear items
item_list.push(new Item('soccergear', 'Nike Ordem Soccer Ball', 'Hit the back of the net with the The Nike® Ordem 3 PL Soccer Ball. The Ordem 3 is the official match ball of the English Premier League for the 2015-2016 season. This FIFA® approved ball features Aerowtrac grooves and a micro-textured casing for accurate flight. The carbon latex bladder and fuse-welded construction allow for an exceptional touch while the vivid visual Power Graphics allow you to track the ball so you can react quickly.', 150.00));
item_list.push(new Item('soccergear', 'Wilson Shinguard', 'Maximize your protection for practice or game day with the Wilson® NCAA® Forte ll Soccer Shinguard. This high impact shinguard is constructed of a removable inner shell for adjustable protection to diffuse impact during elite-level play. Its Lycra® sleeve contains power band enhancements for added compression and blood circulation. Focus on your game with the Wilson® NCAA® Forte ll Soccer Shinguard.', 24.99 ));
item_list.push(new Item('soccergear', 'Adidas Goalie Gloves', 'Protect the goal line with intensity when you sport the adidas® Ace Zones Pro Soccer Goalie Gloves. Evo Zone Technology delivers superior catching and control so you can dominate the game from the net. The negative cut ensures a snug feel while seamless touch features deliver breathability through the latex and foam construction. A stretch-strap wraps your hand to complete the gloves with a comfortable fit.', 114.99));
item_list.push(new Item('soccergear', 'Storelli Exoshield Goalie Jersey', 'Block kicks to the net with maximum mobility in the Storelli® Exoshield GK Adult Goalie Gladiator Jersey. This jersey withstands impact between the posts with polyurethane foam protection at the elbows. For increased comfort, the compression material wicks moisture away to keep the skin cool and dry. Dive and defend without distraction in the lightweight Storelli® Exoshield GK Adult Goalie Gladiator Jersey.', 64.99));
item_list.push(new Item('soccergear', 'Storelli BodyShield Slider Shorts', "Enjoy superior protection with the classic fit of the Storelli® sliders. Lightweight foam padding delivers high-performance protection to keep you safe from impact, swelling and cuts, while the unique design lets you freely move while the pads stay in place. Stay safe on the field with the antimicrobial technology and lightweight padding of the Storelli® Men's Slider Shorts.", 59.99));
item_list.push(new Item('soccergear', 'Adidas Estadio Teamp Backpack', 'Transport your gear to and from the field in style with the adidas® Estadio Team Backpack II. Built with soccer in mind, this backpack is constructed with multiple compartments to conveniently organize and store all of your gear. LoadSpring™ technology adds comfort to the shoulder straps so you can carry more equipment. FreshPAK™ shoe compartment keeps gear fresh throughout the season.', 55.00));
//add videogames
item_list.push(new Item('videogames', 'Star Wars Battlefront', 'Visit classic planets from the original Star Wars™ trilogy, detailed with an unprecedented amount of realism and sense of authenticity that will transport you to a galaxy far, far away', 59.99));
item_list.push(new Item('videogames', 'Just Cause 3', "The Mediterranean republic of Medici is suffering under the brutal control of General Di Ravello, a dictator with an insatiable appetite for power. Enter Rico Rodriguez, a man on a mission to destroy the general's ambitions by any means necessary. With more than 400 square miles of complete freedom from sky to seabed, and a huge arsenal of weaponry, gadgets and vehicles, prepare to unleash chaos in the most creative and explosive ways you can imagine.", 59.99));
item_list.push(new Item('videogames', 'Call of Duty Black Ops III', 'Call of Duty: black Ops III is the ultimate 3-games-in-1 experience. The Campaign you must navigate the hot spots of a new Cold War to find your missing brothers. Multiplayer features a new momentum-based chained movement system, allowing players to fluidly move through the environment with finesse. No Treyarch title would be complete without its signature Zombies offering "Shadows of Evil" has its own distinct storyline right out of the box.', 59.99));
item_list.push(new Item('videogames', 'Fallout 4', 'The epic storylines, adrenaline-pumping action and explosive thrills are back. The Fallout franchise returns with Fallout 4. Grab your controller and get ready to dive back into the enveloping storyline of this legendary series.', 59.99));
item_list.push(new Item('videogames', 'Halo 5: Guardians', 'A mysterious and unstoppable force threatens the galaxy, the Master Chief is missing and his loyalty questioned. Experience the most dramatic Halo story to date in a 4-player cooperative epic that spans three worlds. Challenge friends and rivals in new multiplayer modes: Warzone, massive 24-player battles, and Arena, pure 4-vs-4 competitive combat.*', 59.99));
item_list.push(new Item('videogames', "Assassin's Creed Syndicate", "WELCOME TO THE FAMILY — London, 1868. The Industrial Revolution fattens the purses of the privileged while the working class struggles to survive — until two Assassins rise to lead the world's first organized crime family. Conquer the streets of London. Bring the ruling class to their knees. Make history in a visceral adventure unlike any game you've played before.", 59.99));
// display item list
displayAll();
$('#category').on('change', function(){
// read the selected category using 'value' attribute
var category = $(this).val();
if (category == '0')
displayAll(); // display all items
else
displaySelectedItems(category); // display selected items
// Check all the selected items in the cart
checkCartElements();
});
function checkCartElements(){
$('.addme').each(function(){ // do something with each checkbox
// read its index
var index = $(this).data('index');
// check if the item with a selected index is in the cart
var check = inCart(index); // returns true/false
if(check == true){
$(this).prop('checked', true);
} else {
$(this).prop('checked', false);
}
});
}
function inCart(index){
for (var i=0; i<cart.length; i++){
if (cart[i] == index)
return true;
}
return false;
}
function displaySelectedItems(category){
var itemInfo = '';
/* display data:
use a for loop to go through each element in the item_list array
*/
for (var i=0; i<item_list.length; i++){
// display only selected items
if (item_list[i].type == category){
itemInfo += createItemData(item_list[i], i);
}
// add each item to the table
$('#item-list').html(itemInfo);
}
}
function displayAll(){
var itemInfo = '';
/* display data:
use a for loop to go through each element in the item_list array
Each element is an object.
*/
for (var i=0; i<item_list.length; i++){
// use each item to create HTML content
itemInfo += createItemData(item_list[i], i);
// add each item to the table
$('#item-list').html(itemInfo);
}
}
function createItemData(item, index){
/* Use the data-attribute to add the index of each element of the array so that each checkbox can be mapped to
the corresponding item. Then we can directly use the array of item objects to prepare a suitable
HTML structure and add to the shopping cart.
*/
var trow = "<tr class='itemlist data-index='" +index+ "' >";
trow += "<td class=item-title'><input type='checkbox' class='addme' data-index='"+ index +"' > "+item.title + "</td>";
trow += "<td class='item-description'>"+item.description + "</td>";
trow += "<td class='price'>"+item.price + "</td></tr>";
return trow;
}
$('#item-list').on('click', '.addme', function(){
/* Whenever a item is selected by clicking on any of the checkboxes, perform the following: */
// 1. Read the item index using data- attribute
var index = $(this).data('index');
// 2. If the checkbox is checked then add the item to the cart. Else, remove it from the cart
if ($(this).prop('checked')){
cart.push(index);
} else {
removeItemFromCart(index);
}
// 3. Update the cart list and total credits
displayCartItems();
// update price
calculateTotalPrice();
});
$('#selected-list').on('click', '.delete-item', function(){
var index = $(this).val();
removeItemFromCart(index);
calculateTotalPrice();
checkCartElements();
});
$("#selected-list").on('change', '.quantity', calculateTotalPrice);
function calculateTotalPrice(){
var subtotal = calculateSubtotal();
var tax = calculateTax();
var shipping = calculateShipping();
var total = calculateTotal();
$('#subtotal').html(subtotal.toFixed(2));
$('#tax').html(tax.toFixed(2));
$('#shipping').html(shipping.toFixed(2));
$('#total').html(total.toFixed(2));
}
function calculateSubtotal(){
var subtotal = 0;
var quantity = 1;
for(var i = 0; i < cart.lengh; i++){
quantity = parseInt($(this).val());
var productPrice = parseInt(item_list[cart[i]].price);
subtotal += parseInt(productPrice * quantity);
}
alert(quantity);
return subtotal;
}
function calculateTax(){
var taxAmount = 0;
var taxes = .06;
var subtotal = calculateSubtotal();
taxAmount = parseInt(subtotal * taxes);
return taxAmount;
}
function calculateShipping(){
var shippingAmount = 0;
var shippingPerc = .02;
var subtotal = calculateSubtotal();
shippingAmount = parseInt(subtotal * shippingPerc);
return shippingAmount;
}
function calculateTotal(){
var totalCost = 0;
var subtotal = calculateSubtotal();
var taxes = calculateTax();
var shippingCost = calculateShipping();
totalCost = parseInt(subtotal + taxes + shippingCost);
return totalCost;
}
function removeItemFromCart(index){
// identify and remove the index from the cart and redisplay cart table
var pos = -1;
for (var i=0; i<cart.length; i++){
if (index == cart[i]){
pos = i;
break;
}
}
if (pos>-1){
cart.splice(pos, 1);
// reset the cart table
displayCartItems();
} else {
alert("Could not find!");
}
}
function displayCartItems(){
// create a table row for each item in cart array
var itemInfo = '';
for (var i=0; i<cart.length; i++){
var index = cart[i];
itemInfo += createTableRow(index);
}
$('#selected-list').html(itemInfo);
}
/*function createTableRow(index){
var trow = '';
trow+= "<tr><td>"+item_list[index].title + "</td>>";
trow += "<td>"+item_list[index].price + "</td>";
trow += "<td><input type='text' id='quantity' value='1' size='5' />";
trow += "<td><button type='button' class='delete-item' value='"+index+"'>Delete</button></td></tr>";
return trow;
}*/
function createTableRow(index){
var trow = '';
trow+= "<tr><td>"+item_list[index].title + "</td>>";
trow += "<td>"+item_list[index].price + "</td>";
trow += "<td><input type='text' class='quantity' value='1' size='5' />";
trow += "<td><button type='button' class='delete-item' value='"+index+"'>Delete</button></td></tr>";
return trow;
}
$('#show-cart').on('click', function(){
$('#selected-list').show();
});
$('#hide-cart').on('click', function(){
$('#selected-list').hide();
});
});
</script>
</head>
<body>
<div class='container'>
<div class='row' id='top-box' >
<div class='col-sm-12'>
<h2>Sam's Discount Store</h2>
<h3>Variety of Items!</h3>
</div>
</div>
<div class='row' id='content'>
<div class='col-sm-8'>
<h3 class='title'>Discounted Items</h3>
<h4>
<select id='category'>
<option value='0' >All</option>
<option value='baseballgear' >Baseball Items</option>
<option value='soccergear' >Soccer Items</option>
<option value='videogames'>Video Games</option>
</select>
</h4>
<table class='table table-bordered clmlabels' >
<tr class='t-head'><td >Product</td>
<td >Description</td>
<td >Cost</td>
</tr>
<tbody id='item-list'>
</tbody>
</table>
</div>
<div class='col-sm-4'>
<h2>Cart Items</h2>
<p><button class='btn btn-primary' id='show-cart'>Display cart</button>
<button class='btn' id='hide-cart'>Hide cart</button></p>
<table class='table selected-list' id='selected-list'>
</table>
</div>
<table class='cart-table'>
<tr>
<td>Subtotal: </td>
<td><span id='subtotal'>0</td>
</tr>
<tr>
<td>Tax: </td>
<td><span id='tax'>0</td>
</tr>
<tr>
<td>Shipping: </td>
<td><span id='shipping'>0</td>
</tr>
<tr>
<td>Total: </td>
<td><span id='total'>0</td>
</tr>
</table>
</div>
</div>
<div class='row' id='footer'
<div class='col-sm-12'> <p>Sam's Discount Store</p></div>
</div>
</div>
</body>
</html>

you have misspelled the word length in "cart.lengh" (should be cart.length) (line 185).
Then when calling the method "calculateSubtotal" from the method "calculateTotalPrice", the context gets lost and when you tries to use $(this) to get the quantity. One solution would be to get the input value in the method "calculateTotalPrice" and pass it as parameter to the method "calculateSubTotal".
function calculateTotalPrice(){
var quantity = parseInt($(this).val());
var subtotal = calculateSubtotal(quantity);
}
function calculateTotalPrice(quantity){}

Related

Can I set a function to use different variables depending on how it’s triggered?

I’m making a page to track the quantity/value of 8 sets of assets.
Each Asset will have several buttons that do the same thing just for different Variables depending on the asset that triggers them.
I’m wondering if there is a way to set the variables when it’s triggered or do O just have to copy-paste and edit each function 8 times?
Here is the code I’m starting with it adds A quantity of the asset and removes the cost from the cash value
function buya() {
var quant = document.getElementById("input").value;
quant = Number(quant)
var cost = document.getElementById("a3").innerHTML;
var cash = document.getElementById("cash").innerHTML;
cash = Number(cash)
cost = Number(cost)
var a4 = document.getElementById("a4").innerHTML;
a4 = Number(a4)
console.log(a4)
console.log(quant)
if (cash > cost) {
cash = cash - cost;
a4 += quant;
document.getElementById("cash").innerHTML = cash;
document.getElementById("a4").innerHTML = a4;
} else
alert("you don't have enough money to buy those shares")
}
Basically I want to swap most all of the variables a3, a4 into b2,b4 variables
——edit—— Okay I think I have a fix but it’s clumsy and better ones would be appreciated.
First Add a drop-down to select which asset then replace the things I want to shift with blank variables m, say cuz then at the start of the function add an 8 pronged if/else statement assigning field locations to those variables based on the drop-down selection.
Hate it when the answer pops into my head 5 minutes after posting
——edit ends——
Use function parameters, and then bind event listeners to functions that call it with different parameters.
function buya(costid, a4id) {
var quant = document.getElementById("input").value;
quant = Number(quant)
var cost = document.getElementById(costid).innerHTML;
var cash = document.getElementById("cash").innerHTML;
cash = Number(cash)
cost = Number(cost)
var a4 = document.getElementById(a4id).innerHTML;
a4 = Number(a4)
console.log(a4)
console.log(quant)
if (cash > cost) {
cash = cash - cost;
a4 += quant;
document.getElementById("cash").innerHTML = cash;
document.getElementById(a4id).innerHTML = a4;
} else
alert("you don't have enough money to buy those shares")
}
someElement.addEventListener("click", () => buya("a3", "a4"));
otherElement.addEventListener("click", () => buya("b2", "b4"));
We can pass elements as parameters to the function
function buya(ele1, ele2){
console.log(ele1.innerHTML);
console.log(ele2.innerHTML);
}
<div id="a3">I am a3</div>
<div id="a4">I am a4</div>
<div id="b3">I am b3</div>
<div id="b4">I am b4</div>
<button onClick="buya(document.getElementById('a3'), document.getElementById('a4'));">Click for A3 and A4</button>
<button onClick="buya(document.getElementById('b3'), document.getElementById('b4'));">Click for B3, B4</button>

SharePoint 2013 - Need to Split Names from a Multi-Select People Picker

I'm working in SharePoint 2013, and I have created a custom display template for a Content Search Web Part. Three of my fields use multi-select people pickers, and all three are returning the names in one string as shown below:
Brown, JohnSmith, MikeJones, Mary
I want to return the names in the format shown below but I just can't seem to get it to work:
Brown, John; Smith, Mike; Jones, Mary
I've tried the advice from these blog articles:
https://social.msdn.microsoft.com/Forums/en-US/ea0fe2fe-0757-4c1c-b3cc-2dd99b38bfa1/sharepoint-2013-custom-display-template-people-picker-field-separate-multiple-names-in-display?forum=sharepointdevelopment
https://sharedpointtips.blogspot.com/2015/01/sharepoint-2013-display-template.html
http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/26/useful-javascript-for-working-with-sharepoint-display-templates-spc3000-spc14.aspx
I've tried all the suggestions included in the first article - https://social.msdn.microsoft.com/Forums/en-US/ea0fe2fe-0757-4c1c-b3cc-2dd99b38bfa1/sharepoint-2013-custom-display-template-people-picker-field-separate-multiple-names-in-display?forum=sharepointdevelopment
In the Header:
'Response Preparer'{Response Preparer}:'ResponsePreparerOWSUSER'
In the body:
<script>
$includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
$includeScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Search/jquery-1.11.3.min.js");
$includeScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Search/splitNames.js");
RegisterSod('jquery-1.11.3.min.js', Srch.U.replaceUrlTokens("~sitecollection/_catalogs/masterpage/Display Templates/Search/jquery-1.11.3.min.js"));
RegisterSod('splitNames.js', Srch.U.replaceUrlTokens("~sitecollection/_catalogs/masterpage/Display Templates/Search/splitNames.js"));
//Register Dependencies
RegisterSodDep('splitNames.js', 'jquery-1.11.3.min.js');
AddPostRenderCallback(ctx, function () {
EnsureScriptFunc("splitNames.js", 'splitNames', function() {
var regulatorypartner = $getItemValue(ctx, "Regulatory Partner");
var splitregpartner = "";
splitregpartner = $splitNames(regulatorypartner);
});
});
</script>
In the JavaScript section I have tried this:
var regulatorypartner = ctx.RegulatoryPartnerOWSUSER;
var splitregpartner = splitNames(regulatorypartner);
This is my display code:
<td rowspan="2" colspan="4" style="text-align:center; border:0.5px solid #F88007;"> _#= splitregpartner =#_ </td>
The display should look like this:
Brown, John; Smith, Mike; Jones, Mary
Here is the output of regulatrypartner:
Brown, JohnSmith, MikeJones, Mary
Here is the splitNames code (file is included in the RegisterSod statement):
var newStr="";
for(var i=0;i<str.length;i++){
var char=str.charAt(i);
if(char==char.toUpperCase()){
newStr+=" "+char ;
}else{
newStr+=char;
}
}
return newStr;
}
Keep in your mind that it won't work for a person who has more then 2 capital letters in his name, there is no way to write something that works for all cases and converts to the format you are looking for unless you add an example for multiple words and unique names.
to_ReadableFormat("Brown, JohnSmith, MikeJones, Mary");
function to_ReadableFormat(regulatorypartner){
var counter = 0;
var fullString = "";
regulatorypartner.match(/[A-Z]/g).map(function (cap) { // loop through all the capitals
let regIndex = regulatorypartner.indexOf(cap);
if (counter != 0 && counter % 2 == 0) {
fullString += regulatorypartner.slice(0, regIndex) + ";";
regulatorypartner = regulatorypartner.slice(regIndex, regulatorypartner.length);
}
counter++;
});
fullString += regulatorypartner; // concat the remaining
return fullString;
}

Javascript Array and While Loops

I am very new to javascript and I am trying to list a certain number of job duties on a resume site (dictated by a user input). For example, I ask the user to input the number of job duties they want to see, and using an array and while loop I've been tasked with displaying that many job duties. However, when I click the button, noting is happening. I am not getting any web console errors at all. Here is what I have so far:
<div id="right">
<p> <b> Byrne Dairy</b><br/>
QA Lab Technician<br/>
September 2015 - March 2016<br/><br/><br/>
<button value="Click" onclick="listDuties()">Click</button> to see my top
<input type="text" id="byrne_duties" value="0"/> job duties here:<br/><br/><br/>
<p id="duties"></p>
<script type="text/javascript">
function listDuties() {
var byrneduties = [ "Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/> ",
"Regularly use of Delta Testing Instruments to measure solids and fat contents of products. ",
"Performing the Gerber Method of testing on samples. ",
"Responsible for using the Standard Plate Count method of developing colonies of bacteria. ",
"Interpreting results of bacterial and coliform growth patterns in products. " ];
var x = byrneduties[0];
var text = "";
while (byrneduties[x]) {
text += byrneduties[x] + "<br>";
x++;
document.getElementById('duties').innerHTML = x;
}
}
</script>
</div>
I was told to try and subtract one from the user input, but I'm not sure how to do that. Any help would be great!
Oh, there're some errors in your code:
First of all, your while condition is not a boolean (true/false) but a value of a string array
The var x that you use as index in the loop, is initialized with the first element of string array, then incremented (string+1?!) and finally returned back into an html object (p)
Look the following reviewed code, where I made the above little changes:
function listDuties() {
var byrneduties = ["Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/> ","Regularly use of Delta Testing Instruments to measure solids and fat contents of products. ","Performing the Gerber Method of testing on samples. ","Responsible for using the Standard Plate Count method of developing colonies of bacteria. ","Interpreting results of bacterial and coliform growth patterns in products. " ];
var n = document.getElementById('byrne_duties').value;
var x = 0;
var text = "";
while (x < n) {
text += byrneduties[x] + "<br>";
x++;
}
document.getElementById('duties').innerHTML = text;
}
<div id="right">
<p> <b> Byrne Dairy</b><br/>
QA Lab Technician<br/>
September 2015 - March 2016<br/><br/><br/>
<button value="Click" onclick="listDuties()">Click</button> to see my top
<input type="text" id="byrne_duties" value="0"/> job duties here:<br/><br/><br/>
<p id="duties"></p>
</div>
I hope it was clear, bye.
However, when I click the button, noting is happening. I am not
getting any web console errors at all.
Your current code is simply going to display only one duty since it is over-writing the same div again and again. It is not even taking input from the textbox into consideration.
try this
<div id="right">
<p> <b> Byrne Dairy</b><br/> QA Lab Technician<br/> September 2015 - March 2016<br/><br/><br/>
<button value="Click" onclick="listDuties()">Click</button> to see my top
<input type="text" id="byrne_duties" value="0" /> job duties here:<br/><br/><br/>
<p id="duties"></p>
<script type="text/javascript">
function listDuties() {
var byrneduties = ["Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/> ",
"Regularly use of Delta Testing Instruments to measure solids and fat contents of products. ",
"Performing the Gerber Method of testing on samples. ",
"Responsible for using the Standard Plate Count method of developing colonies of bacteria. ",
"Interpreting results of bacterial and coliform growth patterns in products. "
];
var numberOfDuties = Number( document.getElementById( "byrne_duties" ).value );
if ( !isNaN( numberOfDuties ) && numberOfDuties > 0 )
{
document.getElementById('duties').innerHTML = byrneduties.slice(0, numberOfDuties).join("<br>");
}
}
</script>
</div>
Problem:
var x = byrneduties[0]; // here x is variable which refer to first byrneduties
var text = "";
while (byrneduties[x]) { // here x is index, I think you meant 'i'
text += byrneduties[x] + "<br>";
x++;
document.getElementById('duties').innerHTML = x;
No need to loop, just join array
document.getElementById('duties').innerHTML = byrneduties.join('<br/>');
You could iterate the array with a foreach, like in the following example:
<div id="right">
<p> <b> Byrne Dairy</b><br/>
QA Lab Technician<br/>
September 2015 - March 2016<br/><br/><br/>
<button value="Click" onclick="listDuties()">Click</button> to see my top
<input type="text" id="byrne_duties" value="0"/> job duties here:<br/><br/><br/>
<p id="duties"></p>
<script type="text/javascript">
function listDuties() {
var byrneduties = [ "Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/> ",
"Regularly use of Delta Testing Instruments to measure solids and fat contents of products. ",
"Performing the Gerber Method of testing on samples. ",
"Responsible for using the Standard Plate Count method of developing colonies of bacteria. ",
"Interpreting results of bacterial and coliform growth patterns in products. " ];
var text = "";
byrneduties.forEach( (duty, i)=>{
if ( (i+1) > document.getElementById('byrne_duties').value ) {
return;
}
text += (i+1) + ") " + duty + "<br/>"
});
document.getElementById('duties').innerHTML = text;
}
</script>
</div>
function listDuties() {
var byrneduties = [ "Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/> ",
"Regularly use of Delta Testing Instruments to measure solids and fat contents of products. ",
"Performing the Gerber Method of testing on samples. ",
"Responsible for using the Standard Plate Count method of developing colonies of bacteria. ",
"Interpreting results of bacterial and coliform growth patterns in products. " ];
var x = 0;
var text = "";
var input =document.getElementById('byrne_duties').value
while (x<byrneduties.length && x<input) {
text += byrneduties[x] + "<br>";
x++;
document.getElementById('duties').innerHTML = text;
}
}
<div id="right">
<p> <b> Byrne Dairy</b><br/>
QA Lab Technician<br/>
September 2015 - March 2016<br/><br/><br/>
<button value="Click" onclick="listDuties()">Click</button> to see my top
<input type="text" id="byrne_duties" value="0"/> job duties here:<br/><br/><br/>
<p id="duties"></p>
</div>
Try this
Here is your mistakes.
var x = byrneduties[0]; // x is "Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/>"
var text = "";
while (byrneduties[x]) { // byrneduties["Gathering product samples from sanitized silo and tank ports on a timely schedule. <br/>"] returns undefined
text += byrneduties[x] + "<br>";
x++; // string increment probably returns NaN
document.getElementById('duties').innerHTML = x; //set inner html to NaN.
}
Rewrite it in next way
var x = 0;
var text = "";
while (byrneduties[x]) {
text += byrneduties[x] + '<br/>';
x++;
}
document.getElementById('duties').innerHTML = text ;
Or use array.join
var text = byrneduties.join('<br/>');
document.getElementById('duties').innerHTML = text;

How to sort list by priority in JavaScript?

How to sort list items by priority? This is a to-do list. User can input an item, choose a priority, and add to list.
This is my HTML form:
<input id="task" type="text"/>
<select id="priority">
<option id="Normal">Normal</option>
<option id="Urgent">Urgent</option>
<option id="Critical">Critical</option>
<option id="If You Can">If You Can</option>
</select>
<button onclick="amitFunction()">Add</button>
<hr/>
<table>
<tr>
<th id="result"></th>
<th id="priorit"></th>
</tr>
<table>
This is my JS:
function amitFunction() {
/* Define vars and values */
var lia = document.createElement("p");
var lib = document.createElement("p");
var item = document.getElementById('task').value;
var pro = document.getElementById('priority').value;
var pro_array = ['Urgent','Critical','Normal'];
var item_list = document.createTextNode(item);
var item_pro = document.createTextNode(pro);
lia.appendChild(item_list);
lib.appendChild(item_pro);
/* Check if text is less than 6 chars or more than 42 chars */
if (item.length<6) {
alert('Your text must have a least 6 chars');
} else if (item.length>42) {
alert('Your text must have less than 42 chars');
} else {
document.getElementById("result").appendChild(lia);
document.getElementById("priorit").appendChild(lib);
document.getElementById('task').value='';
}
/* Change text color base on priority */
if (pro==pro_array[0]) {
$("p:last-child").css('color','red');
}
if (pro==pro_array[1]) {
$("p:last-child").css('color','orange');
}
if (pro==pro_array[2]) {
$("p:last-child").css('color','green');
}
/* Delete text when user clicks on it */
$([lia,lib]).click(function(){
$([lia,lib]).css('color','gray');
$([lia,lib]).css("text-decoration", "line-through");
});
}
What I need is, when user adds a new item, it will sort by priority order.
first : Urgent
second : Critical
third : Normal
fourth : If You Can
Each new item that user adds, should be sorted like that. How can I do that?
This is the complete script (JSBin) to understand what I need.
First of all I would suggest you create a new table row every time you create a TODO task, however I decided to keep as much of your code as I could and implement what you asked for. I will admit that it is not the best decision and could be optimized a lot, however I am leaving it as it is simply because there might be many interesting cases in the code that might teach you something new. The sorting is implemented. I hope this helps :)
Your html, left as it was:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<input id="task" type="text"/>
<select id="priority">
<option id="Normal">Normal</option>
<option id="Urgent">Urgent</option>
<option id="Critical">Critical</option>
<option id="If You Can">If You Can</option>
</select>
<button onclick="amitFunction()">Add</button>
<hr/>
<table>
<tr>
<th id="result"></th>
<th id="priorit"></th>
</tr>
<table>
</body>
</html>
and the edited JS code:
//creating a global collection to hold your todo list in memory
var todo_list = [];
function amitFunction() {
var item = document.getElementById('task').value;
/* Check if text is less than 6 chars or more than 42 chars
and return if validation is not passed */
if(item.length<6){
alert('Your text must have a least 6 chars');
return;
}else if(item.length>42){
alert('Your text must have less than 42 chars');
return;
}
var pro = document.getElementById('priority').value;
//keep this for colors
var pro_array = ['Urgent','Critical','Normal'];
//map string priorities to numeric values
var priorities =
{
'Urgent' : 0,
'Critical' : 1,
'Normal' : 2,
'If You Can' : 3
}
//push each new task in the todo list
todo_list.push(
{
priority : pro,
task : item
}
);
//Now this here is perhaps the most important part,
//this is where you sort your todo list based on the
//mapped to numeric values priorities
todo_list.sort(function (task1, task2) {
return priorities[task1.priority] - priorities[task2.priority];
});
//clear the containers holding your list
var resultNode = document.getElementById("result");
while (resultNode.firstChild) {
resultNode.removeChild(resultNode.firstChild);
}
var priorityNode = document.getElementById("priorit");
while (priorityNode.firstChild) {
priorityNode.removeChild(priorityNode.firstChild);
}
//recreate the DOM based on the todo_list collection
for(var i =0; i < todo_list.length; i++)
{
var lia = document.createElement("p");
var lib = document.createElement("p");
var item_list = document.createTextNode(todo_list[i].task);
var item_pro = document.createTextNode(todo_list[i].priority);
lia.appendChild(item_list);
lib.appendChild(item_pro);
document.getElementById("result").appendChild(lia);
document.getElementById("priorit").appendChild(lib);
document.getElementById('task').value='';
/* Change text color base on priority */
if(todo_list[i].priority == pro_array[0]){
$("p:last-child").css('color','red');
}
if(todo_list[i].priority == pro_array[1]){
$("p:last-child").css('color','orange');
}
if(todo_list[i].priority == pro_array[2]){
$("p:last-child").css('color','green');
}
}
//reinitialize the click handlers
var resultNode = document.getElementById("result");
var priorityNode = document.getElementById("priorit");
for(var i =0; i< resultNode.childNodes.length; i++) (function(i){
resultNode.childNodes[i].onclick = function() {
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
}
priorityNode.childNodes[i].onclick = function() {
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
}
})(i);
}
And a working example here:
https://jsbin.com/kudipacexi/edit?html,js,output
In fact there are plenty of approaches, another approach would be to not keep a global collection for your list, instead do the sorting directly using the DOM elements, however you will still have to keep some kind of numeric representation of your priorities in order to sort them by priority. It might also be a good idea to subscribe each of the elements to a single click handler function, then add the line-through style based on the caller of the function. Another thing I'd suggest is, if you are involving jQuery and not focusing on just Vanilla JS, try and use jQuery for the majority of the DOM manipulation.
Because you have a table I suggest to use that as the output.
You can rearrange your select in order to add for each option a priority number and a color attribute like:
<option value="2" color="green">Normal</option>
The table can contain a first column as the current row priority. This column will be hidden.
Each time a new row must be added a sorting process is executed on table rows.
The snippet:
$('button').on('click', function (e) {
var priorityValue = $('#priority option:selected').val();
var priorityText = $('#priority option:selected').text();
var colorVal = $('#priority option:selected').attr('color');
var taskValue = $('#task').val();
if (taskValue.length < 6) {
$('#errMsg').text('Your text must have a least 6 chars');
return;
} else if (taskValue.length > 42) {
$('#errMsg').text('Your text must have less than 42 chars');
return;
}
$('#errMsg').text('');
//
// create the new table row...
//
var newRow = $('<tr/>', {style: 'color:' + colorVal})
.append($('<td/>', {style: "display: none", text: priorityValue}))
.append($('<td/>', {text: taskValue}))
.append($('<td/>', {text: priorityText}));
//
// enlarge current table rows with the current one and sort elements
//
var tableRowsSorted = $('#result tbody').append(newRow).find('tr').get().sort(function(a, b) {
var p1 = +$(a).find('td:first').text();
var p2 = +$(b).find('td:first').text();
return p1 - p2;
});
//
// append/replace the taable body
//
$('#result tbody').append(tableRowsSorted);
//
// reset input text
//
$('#task').val('');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="task" type="text"/><span id="errMsg" style="color: red;"></span>
<select id="priority">
<option value="2" color="green">Normal</option>
<option value="0" color="red">Urgent</option>
<option value="1" color="orange">Critical</option>
<option value="3" color="black">If You Can</option>
</select>
<button>Add</button>
<hr/>
<table id="result">
<thead>
<tr>
<th style="display: none">priority</th>
<th>result</th>
<th>priority</th>
</tr>
</thead>
<tbody>
</tbody>
</table>

Why is JavaScript correctly calculating my variable yet displays it as undefined in the HTML?

And how do I get it to display the number, not undefined?
It's a tipping app. The user inputs the price of whatever it is they bought (pizza, haircut, etc.) in #price, then calcTip() calculates the tip, sends it over to calcTotal() which calculates the total, and sends it over to displayAmounts().
I don't know exactly what happens, but something messes up with the variable tip. calcTip() works correctly and calculates the tip amount successfully. I know this because the JavaScript console displays the amount when I input tip;. However, on the page, #tipSpan displays the tip as undefined.
What baffles me most is that the variable total works perfectly fine.
Does anyone know what might be going on or how I can fix it?
Here is the HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tipping App</title>
<style>
<!-- Temporary -->
#error {
display: none;
}
</style>
</head>
<body>
<div id="wrapper">
<header>
<h1>Tipping App</h1>
</header>
<section>
<div class="price">
<h2>Price Information</h2>
<label for="priceInput">Enter the price below!</label><input id="priceInput" type="text"><button id="calcButton">Calculate the Tip</button>
<p id="error">Error: You need to enter the cost!<br><br>Use only numbers and decimal points, no currency symbols or letters.</p>
</div>
<div id="tipContainer" class="tip">
<h2>Tip Information</h2>
<p id="tipPara">Your tip should be... <span>$<span id="tipSpan"></span></span></p>
</div>
<div id="totalContainer" class="total">
<h2>Total Information</h2>
<p id="totalPara">Your total is... <span>$<span id="totalSpan"></span></span></p>
</div>
</section>
</div>
<script src="js/app.js"></script>
</body>
</html>
Here is the JavaScript:
///// VARIABLES
//////////////////////////////
var priceInput = document.getElementById("priceInput");
var calcButton = document.getElementById("calcButton");
var error = document.getElementById("error");
var tipContainer = document.getElementById("tipContainer");
var tipPara = document.getElementById("tipPara");
var tipSpan = document.getElementById("tipSpan");
var totalContainer = document.getElementById("totalContainer");
var totalPara = document.getElementById("totalPara");
var totalSpan = document.getElementById("totalSpan");
var tip;
var total;
///// FUNCTIONS
//////////////////////////////
function calcTip() {
var price = priceInput.value; // This is the price the user inputs
var minTip = (Math.ceil(price * .15)); // Calculates a 15% tip rounded up to the nearest dollar
var maxTip = (price * .2); // Calculates a 20% tip
if (isNaN(price) || price === "") {
// If the user doesn't enter a number
// Or doesn't enter anything,
// Then display the error message
error.style.display = "block";
return;
} else {
error.style.display = "none";
if (maxTip < minTip) {
// If the 20% tip is less than the 15% rounded tip,
// Then let's go with that 20% tip
calcTotal(price, maxTip);
tip = maxTip;
} else {
// Otherwise, let's just do the 15%
calcTotal(price, minTip);
tip = minTip;
};
};
};
function calcTotal(price, tip) {
// Add the price and the tip together to yield the total
price = parseInt(price);
tip = parseInt(tip);
total = (price + tip);
displayAmounts();
}
function displayAmounts() {
// Update the page to display the tip and the total to the user
tipContainer.style.display = "block";
totalContainer.style.display = "block";
tipSpan.innerText = tip;
totalSpan.innerText = total;
}
///// EVENTS
//////////////////////////////
calcButton.addEventListener("click", calcTip);
Also, unrelated, but does my JavaScript look good? Is it clean code? I hope to find a web development job in the near future, and I know I need to be good at JavaScript.
WORKING DEMO
Update the function arguments
function calcTotal(price, maxTip) {
// Add the price and the tip together to yield the total
price = parseInt(price);
tip = parseInt(maxTip);
total = (price + tip);
displayAmounts();
}
Here the argument tip is overriding the global variable. Replace it to maxTip as you call.
1) In function displayAmounts, pass parameters tip & total
2) Instead of
tipSpan.innerText = tip,
TRY WITH
tipSpan.innerHTML = tip;
and same for total ,
use totalSpan.innerHTML = total
instead of
totalSpan.innerText ;
Then it should work
Try changing your method definition so it passes the values in:
displayAmounts(); should become displayAmounts(tip, total);
See the fiddle here.
Also you should use parseFloat rather than parseInt assuming you'll want to be more accurate than whole numbers.
Just a small mistake!
Instead of:
calcTotal(price, minTip);
tip = minTip;
Do:
tip = minTip;
calcTotal(price, minTip);
This way tip is calculated before displayAmounts is run.
Abut your code:
This is fine with just getting started. I recommend going through w3school's tutorials on javascript. The more you use javascript, the better you will get.
If you want a learning path I would recommend taking, let me know.
innerText works only on IE. textContent is W3C-compliant, but if you want your code to be standards compliant and cross-browser safe you should use this:
while( tipSpan.firstChild ) {
tipSpan.removeChild( tipSpan.firstChild );
}
tipSpan.appendChild( document.createTextNode(tip) );
Do this for totalSpan also.

Categories

Resources