Should all javascript be put inside a load() on body event? - javascript

Should I put all this inside a load() body event so that it loads after the page has rendered and the DOM objects have been created? (I did find I had to use .innerhtml rather than .value for it to work.) If so how...
(*I know this is rubbish code, but it is better than my last attempt and worse than my next attempt. Once I get some time to comeback to this I will recreate it using a literal constructor with internal functions. I am not looking to take this Javascript any further in functionality. The back-end php I have will handle security and checks)
<script type="text/javascript">
//To get the price of a product applying discount
function getPrice(location,quantity)
{
//Get the product choice
var e = document.getElementById("productList["+location+"]");
var productSelected = e.options[e.selectedIndex].value;
//TODO: Determine discounts based on product choice
switch(productSelected)
{
case '0':
return 0;
case '1':
return 10;
case '2':
return 15;
}
return null;
}
//To update product only
function updateProduct(location)
{
updateRow(location,document.getElementById("quantity["+location+"]").value);
}
//To update only that row
function updateRow(location,quantity)
{
//Check Quantity is a valid Number and also not a float or negative
if (!isNaN(quantity) && parseFloat(quantity) && isFinite(quantity) && (quantity >= 0)) {
quantity = Math.floor(quantity);
} else {
quantity = 0;
};
//Update the quantity input field to whatever quantity is - Investigate later!
document.getElementById("quantity["+location+"]").value = quantity;
//Store old Price for changing total
var oldTotal = document.getElementById("totalPrice").innerHTML;
var oldLinePrice = document.getElementById("linePrice["+location+"]").innerHTML;
//Calculate and Store Prices to adjust the total
var productPrice = getPrice(location,quantity).toFixed(2);
var newLinePrice = (quantity*productPrice).toFixed(2);
//Apply Updates
document.getElementById("unitPrice["+location+"]").innerHTML = productPrice;
document.getElementById("linePrice["+location+"]").innerHTML = newLinePrice;
document.getElementById("totalPrice").innerHTML = (oldTotal-oldLinePrice+parseFloat(newLinePrice)).toFixed(2);
}
</script>

You only need to put any getElement calls, or calls to functions that get elements, in a load function.

Well, you could, but that then could delay some js code from getting started working on parts of your .html coding which get loaded first. It also makes code be indented more than it needs to be, causing a readability issue.

Related

How can I get the value of two inputs, calculate them and put the result in another?

I need to refactor this code to improve performance. I take the value of two inputs, and I need to calculate them and display the result in another:
var price;
var percentage;
var value;
function total() {
value = ((price* (percentage/ 100)) + parseFloat(price));
$("#Total").val(value.toFixed(2));
}
$("#price").keyup(function () {
price = $(this).val();
total()
});
$("#percentage").keyup(function () {
percentage = $(this).val();
total()
});
You shouldn't use global variables like this. They could cause race conditions.
// this self executing function will prevent the *Field variables to be bound to your browsers' window variable.
(function() {
let priceField = $("#price");
let percentageField = $("#percentage");
let totalField = $("#Total");
function total() {
let price = priceField.val();
let percentage = percentageField.val();
let value = (price * (percentage/ 100)) + parseFloat(price);
totalField.val(value.toFixed(2));
}
priceField.keyup(function () {
total()
});
percentageField.keyup(function () {
total()
});
})()
I have to say this is a solution for your question, but it's not the nicest one out there. But based on my guess, this solution needs to be simple.
A few extra tips would be to selectively search for input fields like $("input#price") to prevent any other potential collisions, although an id should be unique.
I also would suggest to add some protection in the code. If anybody entered some nonnumeric values, what should happen? Should they be stripped from the input before the calculations are made, or should they trigger an error to the user stating that the user's input is not valid?

Unusual browser error and advice on a Calculate Sum program for javaScript

So I have a problem for class:
Calculate Sums
Create a function called calculateSums that will accept an array of numbers, and return true if the sum of all the positive numbers is greater than or equal to the sum of the absolute value of all the negative numbers.
Method of completion
You should use a while loop in your solution.
It does not matter if you consider 0 a positive or negative number
because it will not change the sum of either side.
After your loop finishes, you should simply return whether the sum of
positive numbers is greater than or equal to the sum of negative
numbers.
It should be possible to call the function as follows:
calculateSums([-1,2]) returns true
calculateSums([-1,2,-3]) returns false
In your code, call your function several times and display the arrays used and the result on your HTML page.
I am having trouble figuring out how to use a while loop to do this. I have one, but it's not right. I am also trying to display the result using "document.getElementByID("message").innerHTML..." and I am getting an error I don't understand.
Here is my code:
/**
* This function calculates the absolute sum of an array of numbers
* #inputs a - an array of numbers
* #returns compare - a boolean
*/
function calculateSum(a) {
//declare variables and set them equal to 0.
var result = 0;
var possum = 0;
var negsum = 0;
var compare;
while (possum >= negsum) {
for (var i = 0; i < a.length; i++) {
var num = a[i];
result = result + Math.abs(num);
if (num%2 == 0) {
possum += result;
} else {
negsum += result;
}
result = 0;
}
if (negsum > possum) {
compare = false;
break;
} else {
compare = true;
break;
}
}
if (compare == true) {
document.getElementById("message").innerHTML = compare;
} else {
document.getElementById("message").innerHTML = compare;
}
return compare;
}
Here is my HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Calculate Sums</title>
<script src = "assignment3.js"></script>
</head>
<body>
<script>
calculateSum([-1,2,3,-2]);
calculateSum([-3,1,-5,2]);
</script>
<p id = "message"></p>
</body>
</html>
I would love someone to help understand the error and offer suggestions of a better way to do this.
Here is the error in the browser:
Uncaught TypeError: Cannot set property 'innerHTML' of null
at calculateSum (assignment3.js:34)
at calculateSums.html:12
I am still very new to coding, so I am often times finding myself frustrated. I really appreciate the help I've found on this site.
Update: I figured out how to fix the while statement.
while (possum >= negsum || negsum > possum)
I'm not sure this is exactly how my teacher imagined it being done because we also have these acceptance criteria:
Calculate Sums
Your code must include a function called calculateSums that accepts
an array of numbers and includes a return statement
Your function must include a while loop.
Your code must use Math.abs() to get the absoluate value of any
negative numbers.
Your code must NOT include an infinite loop.
Your code must conditionally return true or false.
Your code must include multiple variations of tests to show your
function works.
The primary problem in your code is as follows:
When the browser receives the HTML markup in your code, it is processed from top to bottom.
So based on the coed you have shared the code executes in the following order:
The head section loads first as it is on the top. And consequently, your external script assignment3.js also gets loaded. (now the function in the script is available on the global namespace)
<head>
<meta charset="UTF-8" />
<title>Calculate Sums</title>
<script src = "assignment3.js"></script>
</head>
The browser then moves on to the body
<body>
<script>
calculateSum([-1,2,3,-2]);
calculateSum([-3,1,-5,2]);
</script>
<p id = "message"></p>
</body>
First, your script tag executes
<script>
calculateSum([-1,2,3,-2]);
calculateSum([-3,1,-5,2]);
</script>
This works well until this line in the calculateSum function
document.getElementById("message").innerHTML = compare;
Because, at this point, your browser has not gotten around to rendering the <p> tag (the script comes before and executes before this can happen). So document.getElementById("message") can't find the p tag and ends up returning nothing (null). And when you try to access a property on null, you get this error Cannot set property 'innerHTML' of null.
So to fix that, (and as a best practice in general) it's best to put your scripts at the end of the body tag like this (and not in the head):
<body>
<p id = "message"></p>
<script src = "assignment3.js"></script>
<script>
calculateSum([-1,2,3,-2]);
calculateSum([-3,1,-5,2]);
</script>
</body>
This ensures that your page load is not blocked by scripts and also has the side effect that the DOM will have been rendered and ready when your code executes.
Coming to your assignment problem specifically, you can use a while loop to iterate over the array and solve it in a simple manner.
Some change notes:
Move DOM manipulation out of the calculateSum method. This method now has a single clear purpose of calculating the sum and return either true or false.
Write a new function runTestCases which basically creates an array of arrays for the different tests we want to run and runs the calculateSum method for each. It also updates the DOM to reflect the result as stated in your problem statement.
Used while loop to iterate over the array in calculateSum
/**
* This function calculates the absolute sum of an array of numbers
* #inputs a - an array of numbers
* #returns compare - a boolean
*/
function calculateSum(a) {
//declare variables and set them equal to 0.
var result = 0;
var possum = 0;
var negsum = 0;
var currentIndex = 0;
while (currentIndex < a.length) {
var e = a[currentIndex++];
if (e < 0) {
// Negative number
negsum += Math.abs(e);
} else {
// Positive number
possum += e;
}
}
return possum >= negsum;
}
function runTestCases() {
// Array of test cases. (You can add or remove tests as needed)
var tests = [
[-1, 2],
[-1, 2, -3]
];
// Get reference of the list element to show the results
var ul = document.getElementById("result");
// Iterate over the tests aray and update dom to show each result
for (var i = 0; i < tests.length; i++) {
var test = tests[i]; // Get the current test case
var result = calculateSum(test); // Get the result
// Create a new DOM element to display the result
var li = document.createElement('li');
li.innerHTML = JSON.stringify(test) + " <b> " + result + "</b>";
//Appenf newly created element to the list
ul.append(li);
}
}
runTestCases();
<div>
<ul id="result"></ul>
</div>
Here is an answer that I think covers all the requirements:
function calculateSums(nums) {
var posSum = 0,
negSum = 0,
i = 0,
num;
while (i < nums.length) {
num = nums[i];
if (num < 0) {
negSum += Math.abs(num);
} else {
posSum += num;
}
++i;
}
return posSum >= negSum;
}
Uncaught TypeError: Cannot set property 'innerHTML' of null
at calculateSum (assignment3.js:34)
at calculateSums.html:12
You have to call the calculateSum() function after the p tag. It is trying to assign value before it is declare. Put your script before the tag.

Google Script - Forms - Issues Deleting Page Breaks/Sections - "Invalid data updating form"

I am having an issue with the following code when trying to iterate through the items in a form and delete them to make way for new sections/questions. However, I sometimes get the following error "Invalid data updating form". I have worked around this multiple times now, but it keeps coming back up. My current workaround has been to set the section title to "", which made it available to delete. Previously, I didn't need to do this until today.
My question: What is the best way to iterate through the items in a form and delete them from a starting point and not encounter this error?
Reference:
f = the current active form
f_items = all of the items of the form in an array
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date/Time")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
Logger.log(item.getTitle() + " | " + item.getType());
Logger.getLog();
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear);
f_i_len = f.getItems().length-1;
clear++;
}
}
function find(src, name)
{
var s_len = src.length;
for(var iter = 0; iter < s_len; iter++)
{
var s = src[iter].getTitle();
if(s == name)
{ return iter; }
}
return -1;
}
The issue I had with this was that the PageBreakItem I was trying to delete was the destination for a conditional answer earlier in the form.
Below is my code where I needed to delete everything after a certain item, which linked to the sections I needed to delete, so I was able to iterate backwards with a while loop.
function getParentNameItem_(form, form_items){
//finds the item with conditional navigation to what you want to delete
var parent_name_item = find_(form_items, "Your Name");
parent_name_item = parent_name_item.asListItem();
//clears all choices which breaks the navigation dependency
//this frees up the items to be deleted
parent_name_item.setChoices([parent_name_item.createChoice("")]);
var size = form_items.length - 1;
//iterates from the end back to the last question I want to keep
while(form_items[size].getTitle() != "Your Name"){
//this can take either the item itself or the index as I've done
form.deleteItem(size);
size--;
}
/*I rebuild the choices for parent_name_item
later based on information from a spreadsheet
which I also use to determine the content of
the PageBreakItems I just deleted*/
return parent_name_item;
}
I found out the issue! It was the clear++ at the end of the loop. With the number of items in the going down with each iteration, the clear++ was causing it to skip over the page break items. Below is my finished code:
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear); //}
f_i_len = f.getItems().length-1;
}
}

Random Items and linking

Sorry if the post was unclear, I’ll try to explain as best I can. I’m creating a social psychology experiment online, and I need a function to be able to select at random different names (like John, Mike, Alex etc.). While looking for help online I found this code:
function swapImages(){
var $active = $('#myGallery .active');
var $next = ($('#myGallery .active').next().length > 0) ? $('#myGallery.active').next() : $('#myGallery img:first');
$active.fadeOut(function(){
$active.removeClass('active');
$next.fadeIn().addClass('active');
});})
Using this code and the "mousetrap" library I was able to change the name when a key is pressed. But I have no clue on how I can make the names appear randomly (this means, not in the order they are on the code, but different every time I do it). And after 40 different names, I need to link to another html page.
Thanks for the help, and sorry if my last post was confusing.... This is my first approach to programing :)
Old post:
Im quite new to the programing world, i need some help making this code select random items, not in the order I put them on. Also, i got this from the web and i need it to stop after 40 items and link to antoher page.
Thanks for the help
If you simple need to select random names - selection from array using Math.Random is the simplest approach:
var names = ["John", "Mike", "Peter", "Sid", "Homer"]
var idx;
do {
idx = parseInt(names.length * Math.random());
alert(names[idx]);
names.splice(idx, 1);
} while (names.length > 0)
Basically it generates random index within boundaries of array's length, selects element at that index and then removes the element from array. Loop exits when there're no more elements to display.
Demo: http://jsfiddle.net/4NNTA/1/
If your list has over 40 items and you need to exit after 40 - add a counter and condition to the while. After exiting the loop you can redirect to another page by setting location.href to URL of page you want to go to.
UPDATE This is a function that used revised code above. It lets you specify arbitrary number of names:
var Names = function () {
var data;
var counter;
function initData() {
data = ["John", "Mike", "Peter", "Sid", "Homer"]
}
this.init = function (c) {
counter = c;
initData()
}
this.getName = function () {
if (counter === 0) {
return ""
} else {
if (data.length === 0) initData();
var idx = parseInt(data.length * Math.random());
var sName = data[idx]
data.splice(idx, 1);
counter--;
return sName
}
}
}
Inside of function initData you can specify array of names. Then you initialize it by passing number of names you want to display (this example initializes it to 40):
var myNames = new Names();
myNames.init(40);
And then every time you call
myNames.getName()
It will give you next random name. The names will not repeat until data is exhausted - then the array is reinitialized and random un-repeated names begin again. When all 40 names are retrieved - function returns empty string, which you can check and act accordingly.

Trouble with Variable value in function

I have the following script where a variable gets its value from an input field, however when I run my function its not working, returns nothing. Im new to JS so im unsure if it needs to be part of a function *even though Ive tried this with no luck) or what...
/////////////////////////////////////////////////////////////////////
// Variables
// Content/SLA
var ContentMinutes = '';
var ContentMinutesSelector; // Switch Case
var ServiceLevel = 5;
var NoOfFrames = 2;
// Render Time (Hairier the Better)
var AvgFrameRenderTime = '';
var AvgFrameRenderTimeSelector = 10; // Switch Case
var CoresInTest = document.getElementById('CoresInTest').value;
// Other
var EstimatedCoreHours = NoOfFrames * CoresInTest * AvgFrameRenderTimeSelector;
// Cost Estimate
var CostEstimate = ServiceLevel * EstimatedCoreHours;
/////////////////////////////////////////////////////////////////////
// Functions
function CalculateEstimate() {
// Estimate Cost
parseInt(document.getElementById("PriceEstimate").innerHTML=CostEstimate.toFixed(2));
// Estimate Core Hours
parseInt(document.getElementById("EstimatedCoreHours").innerHTML=EstimatedCoreHours.toFixed( 2));
}
my PriceEstimate and EstimatedCoreHours fields are both just empty divs, <div id="EstimatedCoreHours"></div>, My calculations work if i define a value for the variable as opposed to document.getElementById so I believe I must need to run a function or something to update all the vartiables?
But if I set...
var CoresInTest = document.getElementById('CoresInTest').value;
to
var CoresInTest = 10;
Then it works fine...
Its not actually my return, the problem is my variables arent calling, IF i define them with a number then it works.
I guess you need to do something like this, if you are looking to get calculated data in your div.
document.getElementById("PriceEstimate").innerHTML=parseInt(CostEstimate.toFixed(2));
// Estimate Core Hours
document.getElementById("EstimatedCoreHours").innerHTML=parseInt(EstimatedCoreHours.toFixed(2));
If var CoresInTest = 10; works fine, then your code is placed wrong.
What element is CoresInTest? is it a text field? and if so is this script placed or called before the element renders? then you will have to reinitialize that variable.
If PriceEstimate and EstimatedCoreHours are elements you should use the value property
this might work for you:
document.getElementById("PriceEstimate").value=parseInt(CostEstimate.toFixed(2),10);
document.getElementById("EstimatedCoreHours").value=parseInt(EstimatedCoreHours.toFixed(2),10);
If var CoresInTest = 10; makes it work fine, then it must be something to do with document.getElementById('CoresInTest').value - so why isn't that working? Is it a drop down list? Instead of us guessing, tell us.

Categories