JavaScript getElementById null values error and how to avoid it - javascript

I have created a form where user inputs the minimum and maximum number of textareas that can be added to a page. It generates the code below in order to pick it up with JScript:
$minimum .= '<div id="K'.$i.'">'.$min[$i].'</div>';
$maximum .= '<div id="H'.$i.'">'.$max[$i].'</div>';
In JScript, I defined a loop that will catch the above div tags and theirs innerHTMLs as numbers and put it in an array.
var mincount = [];
var maxcount = [];
for(var k=1; k<101; k++) {
mincount[k] = parseInt(document.getElementById("K"+k).innerHTML, 10);
maxcount[k] = parseInt(document.getElementById("H"+k).innerHTML, 10);
}
The k is defined to be up to 100. So, I know this is the problem, because then if there are less then 100 textareas, the getElementById is having null values.
So, it gives error:
Uncaught TypeError: Cannot read property 'innerHTML' of null
But it must be defined to be up to 100. So, is there any option that can work something like this:
if(mincount[k] == null) {mincount[k] = ""} // in order to avoid null values.
This isn't working. It still get an error.

The easier solution would be to give those elements the same class, and then just select all elements with that class. I don't see a reason why every element must have their own ID.
$minimum .= '<div class="K">'.$min[$i].'</div>';
$maximum .= '<div class="H">'.$max[$i].'</div>';
Then you do in JavaScript:
var mincount = [];
var minelements = document.querySelectorAll('.K');
for (var i = 0, l = minelements.length; i < l; i++) {
mincount.push(parseInt(minelement[i].innerHTML, 10));
// if the content is purely numeric, unary plus is simpler:
// mincount.push(+minelement[i].innerHTML);
}
Same of maxcount.
Or as #adeneo suggested, if your JavaScript code is also served through PHP and you have access to the $min array, just do
var mincount = <?php echo json_encode($min); ?>;
Then you don't even how to touch the DOM.

The simplest way to test for null values is a simple equality check.
...
...
for(var k=1; k<101; k++) {
var K_el = document.getElementById("K"+k);
var H_el = document.getElementById("H"+k);
if ( K_el != null && H_el != null ) {
mincount[k] = parseInt(K_el.innerHTML, 10);
maxcount[k] = parseInt(H_el.innerHTML, 10);
}
}
Unless these values can be modified by the user, it would be easier to calculate the min's and max's in PHP.

A preferred solution is to just put a common class on each element of a given type and query the elements that actually exist:
$minimum .= '<div class="K" id="K'.$i.'">'.$min[$i].'</div>';
$maximum .= '<div class="H" id="H'.$i.'">'.$max[$i].'</div>';
var mincount = [];
var maxcount = [];
var kItems = document.querySelectorAll(".K");
var hItems = document.querySelectorAll(".H");
for (var i = 0, len = Math.min(kItems.length, hItems.length); i < len; i++) {
mincount[i] = parseInt(kItems[i].innerHTML, 10);
maxcount[i] = parseInt(hItems[i].innerHTML, 10);
}
Without changing the HTML and keeping with your current code approach, you can just check to see if an element exists before trying to use it.:
var mincount = [];
var maxcount = [];
var objK, objH;
for(var k=1; k<101; k++) {
objK = document.getElementById("K"+k);
objH = document.getElementById("H"+k);
if (objK) {
mincount[k] = parseInt(objK.innerHTML, 10);
}
if (objH) {
maxcount[k] = parseInt(objH.innerHTML, 10);
}
}
Or, if you know that as soon as one object doesn't exist, you should break out of the for loop, you can do this:
var mincount = [];
var maxcount = [];
var objK, objH;
for(var k=1; k<101; k++) {
objK = document.getElementById("K"+k);
objH = document.getElementById("H"+k);
if (!objK || !objH) {
break;
}
mincount[k] = parseInt(objK.innerHTML, 10);
maxcount[k] = parseInt(objH.innerHTML, 10);
}

Related

Why isn't querySelectorAll working with my ID values?

I use this piece of code for a live searching and for calculate the "Total" :
$('#search').keyup(function () {
var x = [];
var y = [];
var $total = 0;
var inputString = $("#search").val();
var act = document.getElementById("labdb").value;
$('td:first-child').parent().show();
$('td:first-child').parent().addClass("notHide");
$('td:first-child').parent().removeClass("toHide");
$var = $('td:first-child').parent('tr:not(:icontains(' + inputString + ')):not(:contains("Total"))');
$var.hide();
$var.addClass('toHide');
$var.removeClass('notHide')
for (var price of document.querySelectorAll('tr.notHide td.price')) {
x.push(parseFloat(price.innerText));
}
for (var cantidad of document.querySelectorAll('tr.notHide td.quantity')) {
y.push(parseFloat(cantidad.innerText));
}
for(var i = 0; i <= x.length-1; i++){
$total += x[i] * y[i];
}
document.getElementById("total"+act).innerText = $total.toFixed(2);
});
I have various tables, with differents ID's, first is id='0', second is id='1', etc.
I use collapse ('show') or ('hide') for show the selected table with a selector.
So, the problems comes when I try to calculate the "Total", It's calculated with the value of the class "notHide" and "price/quantity".
And all the tables get this Class, so the price got crazy if there is more than 1 table with "Total"
I KNOW, that I need to specify the ID of the table to work around, but I cant.
I have tested with:
var act = document.getElementById("labdb").value;
var actHTML = document.getElementById("labdb");
It get the ID from the selected table, and then, in this part of code include it:
for (var price of actHTML.document.querySelectorAll('tr.notHide td.price')) {
x.push(parseFloat(price.innerText));
}
for (var cantidad of actHTML.document.querySelectorAll('tr.notHide td.quantity')) {
y.push(parseFloat(cantidad.innerText));
}
But that, dont work, Im all the time thinking about other solutions and trying it, but I cant.
Its the final part of that page. Only need to specify the ID of the "Total" calculator.
I had tried to with:
for (var price of document.querySelectorAll('#'+act+' tr.notHide td.price')) {
x.push(parseFloat(price.innerText));
}
for (var cantidad of document.querySelectorAll('#'+act+' tr.notHide td.quantity')) {
y.push(parseFloat(cantidad.innerText));
}
For be more specific, I need that document.querySelectorAll('tr.notHide td.quantity') comes from a specific ID. For the rest, works perfectly, inclusive deleting the others tables with the parameter Total
EDIT_0:
I have do that:
let myTable = document.querySelector("#"+act);
let all = myTable.querySelectorAll('tr.notHide td.price');
for (var price of all) {
x.push(parseFloat(price.innerText));
}
for (var cantidad of all) {
y.push(parseFloat(cantidad.innerText));
}
for (var i = 0; i <= x.length - 1; i++) {
$total += x[i] * y[i];
}
from here: LINK
And that is the console error:
scripts.js:57 Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#0' is not a valid selector.
at changePrice (https://neutrino.ugr.es/js/scripts.js:57:28)
at HTMLSelectElement. (https://neutrino.ugr.es/js/scripts.js:15:5)
at HTMLSelectElement.dispatch (https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js:2:43336)
at y.handle (https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js:2:41320)
Well I found the error. The method document.querySelectorAll('') doesn't work if the ID starts with a digit. Like said in an article on the subject:
The spec defines identifiers using a token diagram. They may contain
the symbols from a to z, from A to Z, from 0 to 9, underscores (_),
hyphens -, non-ASCII symbols or escape sequences for any symbol.
They cannot start with a digit, or a hyphen (-) followed by a digit. Identifiers require at least one symbol (i.e. the empty
string is not a valid identifier).
This is the final code:
$('#search').keyup(function () {
var inputString = $("#search").val();
$('td:first-child').parent().show();
$('td:first-child').parent().addClass("notHide");
$('td:first-child').parent().removeClass("toHide");
$var = $('td:first-child').parent('tr:not(:icontains(' + inputString + ')):not(:contains("Total"))');
$var.hide();
$var.addClass('toHide');
$var.removeClass('notHide')
changePrice();
});
function changePrice() {
var x = [];
var y = [];
var $total = 0;
var act = document.getElementById("labdb").value;
actt="t"+act+act;
//alert(act);
var myTable = document.querySelector("#"+actt);
var allx = myTable.querySelectorAll('tr.notHide td.price');
var ally = myTable.querySelectorAll('tr.notHide td.quantity');
//alert(all)
for (var price of allx) {
x.push(parseFloat(price.innerText));
}
for (var cantidad of ally) {
y.push(parseFloat(cantidad.innerText));
}
for (var i = 0; i <= x.length - 1; i++) {
$total += x[i] * y[i];
}
//alert(x.length)
document.getElementById("total" + act).innerText = $total.toFixed(2);
}
I changed the ID to start with a t: actt="t"+act+act;
(act+act is because id is 00, not 0)

Looping through a table rows with comparisons using VUgen

I have a table I need to compare each of the values within to an existing parameter.
I have this Xpath here: //*[#id="maincontent"]/messages/div/div[1]/div[1]/table/tbody/tr[1]/td[4]/div/span
and would like to insert an increment variable from the loop to go through the /tr/ in the table only.
Here is what i have so far:
var i;
var max = 10;
var xpathleft = `*[#id="maincontent"]/messages/div/div[1]/div[1]/table/tbody/tr[`;
var xpathright = `]/td[4]/div/span`;
for (i = 1; i < max, i++)
{
var currentXpath = string.concat(xpathleft, i, xpathright);
}
if (currentXpath.innerHTML == PartnerIDs)
{
lr_log_message("Match Found!");
}
This is currently sitting in an Evaluate Javascript step in TruClient/VUgen and is giving me Syntax Error: Unexpected Token )
The element here doesn't have any ID I can reference and looks like this: Partner ID
and has been difficult to pull the needed Partner ID text within code.
Some of your JavaScript syntax is incorrect.
Try this:
var i;
var max = 10;
var xpathleft = `*[#id="maincontent"]/messages/div/div[1]/div[1]/table/tbody/tr[`;
var xpathright = `]/td[4]/div/span`;
for (i = 1; i < max; i++){
var currentXpath = `${xpathleft}${i}${xpathright}`;
if (currentXpath.innerHTML == PartnerIDs) {
lr_log_message("Match Found!");
}
}

How to shorten these duplicate JavaScript code into a loop?

I had ten rows which each rows contain 4 column, now I want to get the value which I had import using localStorage. I find a way to put all these value independently but the code is all the repeat one. These will cause to redundancy of code. I wonder if there are a way to shorten the code using loop?
Here is my code
var res = {};
$(function(){
$('#subbtn').click(function() {
console.log($('#tab').find('tr'))
$('tr').each(function(){
var tmp = [];
var cl ;
$(this).find('select').each(function(){
cl = $(this).attr('class');
//console.log(cl);
tmp.push($(this).val());
})
res[cl] = tmp
})
console.log(res);
localStorage.setItem("testingvalue",JSON.stringify(res));
document.getElementById("results__display").innerHTML = (localStorage.getItem("testingvalue"));
})
})
$( document ).ready(function(){
var res = {};
try {
console.log('existed');
res = JSON.parse(localStorage.getItem("testingvalue"));
//alert(res.r1[2]);
document.getElementsByClassName("r1")[0].selectedIndex=res.r1[0];
document.getElementsByClassName("r1")[1].selectedIndex=res.r1[1];
document.getElementsByClassName("r1")[2].selectedIndex=res.r1[2];
document.getElementsByClassName("r1")[3].selectedIndex=res.r1[3];
document.getElementsByClassName("r2")[0].selectedIndex=res.r2[0];
document.getElementsByClassName("r2")[1].selectedIndex=res.r2[1];
document.getElementsByClassName("r2")[2].selectedIndex=res.r2[2];
document.getElementsByClassName("r2")[3].selectedIndex=res.r2[3];
document.getElementsByClassName("r3")[0].selectedIndex=res.r3[0];
document.getElementsByClassName("r3")[1].selectedIndex=res.r3[1];
document.getElementsByClassName("r3")[2].selectedIndex=res.r3[2];
document.getElementsByClassName("r3")[3].selectedIndex=res.r3[3];
document.getElementsByClassName("r4")[0].selectedIndex=res.r4[0];
document.getElementsByClassName("r4")[1].selectedIndex=res.r4[1];
document.getElementsByClassName("r4")[2].selectedIndex=res.r4[2];
document.getElementsByClassName("r4")[3].selectedIndex=res.r4[3];
document.getElementsByClassName("r5")[0].selectedIndex=res.r5[0];
document.getElementsByClassName("r5")[1].selectedIndex=res.r5[1];
document.getElementsByClassName("r5")[2].selectedIndex=res.r5[2];
document.getElementsByClassName("r5")[3].selectedIndex=res.r5[3];
document.getElementsByClassName("r6")[0].selectedIndex=res.r6[0];
document.getElementsByClassName("r6")[1].selectedIndex=res.r6[1];
document.getElementsByClassName("r6")[2].selectedIndex=res.r6[2];
document.getElementsByClassName("r6")[3].selectedIndex=res.r6[3];
document.getElementsByClassName("r7")[0].selectedIndex=res.r7[0];
document.getElementsByClassName("r7")[1].selectedIndex=res.r7[1];
document.getElementsByClassName("r7")[2].selectedIndex=res.r7[2];
document.getElementsByClassName("r7")[3].selectedIndex=res.r7[3];
document.getElementsByClassName("r8")[0].selectedIndex=res.r8[0];
document.getElementsByClassName("r8")[1].selectedIndex=res.r8[1];
document.getElementsByClassName("r8")[2].selectedIndex=res.r8[2];
document.getElementsByClassName("r8")[3].selectedIndex=res.r8[3];
document.getElementsByClassName("r9")[0].selectedIndex=res.r9[0];
document.getElementsByClassName("r9")[1].selectedIndex=res.r9[1];
document.getElementsByClassName("r9")[2].selectedIndex=res.r9[2];
document.getElementsByClassName("r9")[3].selectedIndex=res.r9[3];
document.getElementsByClassName("r10")[0].selectedIndex=res.r10[0];
document.getElementsByClassName("r10")[1].selectedIndex=res.r10[1];
document.getElementsByClassName("r10")[2].selectedIndex=res.r10[2];
document.getElementsByClassName("r10")[3].selectedIndex=res.r10[3];
}
catch (error){
console.log(error.message);
}
});
Looking at this repeated line:
document.getElementsByClassName("r1")[0].selectedIndex=res.r1[0];
...a simple first pass improvement would be to just use a nested for loop with variables instead of "r1" and 0:
for (var r = 1; r <= 10; r++) {
for (var i = 0; i < 4; i++) {
document.getElementsByClassName("r" + r)[i].selectedIndex = res["r" + r][i];
}
}
Notice, though, that this means the .getElementsByClassName("r" + r) call happens four time for each value of r, which is not very efficient - it would be better to move that into the outer loop:
var els;
for (var r = 1; r <= 10; r++) {
els = document.getElementsByClassName("r" + r);
for (var i = 0; i < 4; i++) {
els[i].selectedIndex = res["r" + r][i];
}
}
In the second version the inner loop could say i < els.length rather than i < 4, although note that either way you need to be sure you match the number of HTML elements to the number of items in your res object.
You've seem to have the jQuery library loaded. Using jQuery makes this much easier.
Here is an example:
var res = JSON.parse(localStorage.getItem("testingvalue"));
$("tr select").each(function(){
$(this).val(res[$(this).attr("class")][$(this).index()]);
});
Of course, this will only work if the select elements have only one class name and the res object contains values for all the select elements that are inside tr elements. Based on the jQuery code in your question that seems to be the case.
And this is a safer approach
Object.keys(res).forEach(function(key){
res[key].forEach(function(val, index){
$("tr select." + key).eq(index).val(val);
});
});
Code below will work regardless the size of your data in storage:
res = JSON.parse(localStorage.getItem("testingvalue"));
// Let's start with checking 'res' type.
// - if it's an Array, get the the length from .length
// - if it's Object, get the the length from Object.keys().length
var resLength = Array.isArray(res) ? res.length : typeof res === 'object' ? Object.keys(res).length : 0;
// loop throw the rows.
for (var i = 0; i < resLength; i++) {
// Do the same as above: get type of the row and calculate it length for the loop.
var rowLength = Array.isArray(res[i]) ? res.length : typeof res[i] === 'object' ? Object.keys(res[i]).length : 0;
// loop throw the columns on the row.
for (var j = 0; j < rowLength; j++) {
document.getElementsByClassName('r'+i)[j].selectedIndex=res['r'+i][j];
}
}

Google Apps Script - TypeError: Cannot read property "0" from undefined

I keep getting an "undefined error" on this line of code:
while (agentSheetValues[j][0] != "") {
... even though if I use Logger.log(agentSheetValues[j][0]) it outputs the variable, so I know it IS defined. I've also tried replacing the while loop with a for loop and have the same problem; what's going on?
Full code below, thank you.
var resultSS = SpreadsheetApp.openById('someKey');
var agentSheet = resultSS.getSheetByName('Agents');
var agentSheetRows = agentSheet.getDataRange();
var agentSheetValues = agentSheetRows.getValues();
var agentSheets = [];
var j = 1;
while (agentSheetValues[j][0] != "") {
var sheetKey = agentSheetValues[j][1];
agentSheets.push(sheetKey);
j++
}
You should consider in your loop that your array of data may end before you reach an empty value. Try this:
var resultSS = SpreadsheetApp.openById('someKey');
var agentSheet = resultSS.getSheetByName('Agents');
//getDataRange returns from A1 to the last column and row WITH DATA.
//you'll not get the whole sheet. Blank rows and columns after the end are ignored
var agentSheetDataRange = agentSheet.getDataRange();
var data = agentSheetDataRange.getValues();
var agentSheets = [];
//I'm assuming you started 'j = 1' on purpose, to skip the header row
//Also, if you're sure you do not have another column with more data than A
// you could drop the:: && data[j][0] != ''
for( var j = 1; j < data.length && data[j][0] != ''; ++j )
agentSheets.push(data[j][1]);

javascript summing up array

I`m trying to sum the values of the elements in an array using javascript, this is my script.
function sumAll()
{
var totalOverheads = 0;
var overheads = new Array();
overheads = document.getElementsByName('overhead');
for(i=0;i<overheads.length;i++)
if(!isNaN(overheads[i].value) || overheads[i].value != null || overheads[i].value != "" || overheads[i].value != '' || overheads[i].value != NULL)
alert(overheads[i].value);
//totalOverheads = parseInt(totalOverheads) + parseInt(overheads[i].value);
alert(totalOverheads);
}
for now, in the if condition inside the for loop, I`m displaying the value of the item in an alert, yet it`s not working correctly, it just displays all the items even if the item is not a number, how can I perform an operation if the input is only a number?
getElementsByName returns a NodeList. Not sure if that was the problem, but anyway:
var totalOverheads = 0;
var overheads = document.getElementsByName('overhead');
var n;
var i; // <<--- don't forget to initialise i
for (i = 0; i < overheads.length; ++i) {
n = parseInt(overheads.item(i).value, 10);
if (!isNaN(n)) {
totalOverheads += n;
}
}
alert(totalOverheads);
Also, please use brackets!

Categories

Resources