I have a function which works just fine the first if I call it just once, but when I call it repeatedly within a for loop, I get the following error:
TypeError: getNamedRange is not a function, it is string.
Doing a search on this error gives me a clue that this is a javascript error, not a Google Apps Script error. I haven't worked much with javascript, but I suspect it may have something to do with how I return the value from the function.
This is the code which calls the function:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var baseSheet = ss.getSheetByName("Base");
var catCol = 9;
var riskAreaColumn = 10;
var numRows = baseSheet.getDataRange().getNumRows();
// I am not using this var, should I be?
var data = baseSheet.getDataRange().getValues();
var cell;
var rangeName;
var range;
var rule;
for(var i=2; i<numRows; i++){
cell = baseSheet.getRange(i, riskAreaColumn);
rangeName = getNamedRange("CategoryRiskRange",baseSheet.getRange(i, catCol).getValue());
range = SpreadsheetApp.getActiveSpreadsheet().getRangeByName(rangeName);
rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cell.setDataValidation(rule);
}
SpreadsheetApp.flush();
}
This is the function being called:
function getNamedRange(categoryRange, category) {
var categoryList = SpreadsheetApp.getActive().getRangeByName(categoryRange).getValues();
for (var i = 0; i < categoryList.length; i++) {
if (categoryList[i][0] == category) {
getNamedRange = categoryList[i][1];
return getNamedRange;
}
}
}
The first time through the for loop works, the second time gives me the aforementioned error. Thank you for reading this, I hope it's clear.
you are overwriting the function definition here:
getNamedRange = categoryList[i][1];
this will work:
if (categoryList[i][0] == category) {
return categoryList[i][1];
}
Javascript doesn't interpret things until it gets to them, and is very happy to redefine things when you tell it to.
The first time through, it sees
function getNamedRange(categoryRange, category)
and says "oh, a function! Cool!" But in that function, you have the line
getNamedRange = categoryList[i][1];
and it says "Oh, so getNamedRange is something else now. Okay, I'm fine with that."
Rename your variable, and you should be fine.
Related
I have referenced multiple SO questions but I still could not find a solution. These are the questions I took a look at (main ones):
Pass a JavaScript function as parameter
How to execute a method passed as parameter to function
compute.js:
const mainTable = document.getElementById('nonFixedSample');
function getRows(metricName) {
let row = 0;
let z = document.getElementsByTagName('tr');
for (let i = 0; i < z.length; i++) {
if (mainTable.rows[i].firstChild.textContent === metricName) {
row = i;
return row;
}
}
}
// Here I am trying to pass that function as callback
function stdCellArea(callback) {
rowNumber = callback();
let runs = mainTable.rows[rowNumber].cells.length;
// Other code
}
Now I am calling it, reg_report.php:
<script>
stdCellArea(function() {
getRows('test');
});
</script>
But I get the following error:
Uncaught TypeError: Cannot read property 'cells' of undefined
at stdCellArea (compute.js:17)
at reg_report.php:39
Basically, I need to use return value of getRows() as an argument for stdCellArea(). I know I could simply do this:
let x = getRows('text');
stdCellArea(x);
But I have to call this function over 10 times, so I do not want to create many variables. Who can help?
You need to return the value from your callback: return getRows('test');. Without that, rowNumber becomes undefined as that's what functions without an explicit return, return.
This may seem like a duplicate question, and to some extent, it is, but I have already been through many similar questions, and sadly, none have suited my need. I would really appreciate problem-specific advice.
My main problem in the JavaScript code here is that I cannot access the values in the variables RememberText20 and RememberFullText, in function TextLimiter, from function ReadMoreLessText. The "Message" is an argument for the ReadMoreLessText function, which essentially matches the element clicked to the correct value in the aforementioned variables, which are themselves arrays.
*I know there is nothing wrong with the arrays themselves, as they retain their values as they are supposed to, because a simple alert() proves this. Similarly, there is nothing wrong with the Message argument, as the function ReadMoreLessText works fine with other values.
My simple problem is that I cannot access the values in the aforementioned variables, from the ReadMoreLessText function, although they are global variables, as they should be.
I would really appreciate a problem-specific answer here. Thank you in advance.
// JavaScript Document
//Start Text250
window.onload = function TextLimiter() {
for (y = 0; y < 6; y++) {
FullText = document.getElementsByClassName("Introduction")[y].innerHTML;
TextLength = FullText.length;
RememberFullText = [];
RememberFullText[y] = FullText;
var Text250 = FullText.substr(0, 250) + "...";
RememberText250 = [];
RememberText250[y] = Text250;
if (TextLength > 250) {
document.getElementsByClassName("Read_More")[y].innerHTML = "Read More→";
document.getElementsByClassName("Introduction")[y].innerHTML = Text250;
} else {
document.getElementsByClassName("Read_More")[y].innerHTML = "";
}
}
};
//End Text250
//Start ReadMoreLessText
var ReadMore = function(Message) {
var ScreenText = document.getElementsByClassName("Introduction")[Message].innerHTML;
if (ScreenText === RememberText250[Message]) {
document.getElementsByClassName("Introduction")[Message].innerHTML = RememberText250[Message];
} else {
document.getElementsByClassName("Introduction")[Message].innerHTML = RememberText250[Message];
}
};
//End ReadMoreLessText
Try defining RememberFullText and RememberText250 outside the enclosing for loop.
window.onload = function TextLimiter() {
RememberFullText = [];
RememberText250 = []
for (y = 0; y < 6; y++) {
...
As written they are set to an empty array in each iteration of the loop. Hence only the last entry of each array will be retained after the loop has finished.
I don't see your variables declared as globals. Do you have a var RememberText20, RememberFullText; outside any function?
I am trying to divide 1 by the console.count() every time it is used. However, this code does not work.
var counter = console.count();
console.log(1/counter);
Any suggestions on how I could do this? I tried doing parseInt but no luck.
Way to save console.count() as an integer?
No. console.count() does not return anything, it directly prints to the console, just like console.log().
Simple implementation of console.count:
var count = (function() {
var counter = {};
return function(v) {
return (counter[v] = (counter[v] || 0) + 1);
}
}());
console.log('foo', count('foo'));
console.log('foo', count('foo'));
console.log('bar', count('bar'));
Here is a script that intercepts messages sent to the console.
var counter = 0;
function takeOverConsole(){
var console = window.console
if (!console) return
function intercept(method){
var original = console[method]
console[method] = function(){
var message = Array.prototype.slice.apply(arguments).join(' ')
// do sneaky stuff
if (original.call){
// Do this for normal browsers
original.call(console, message)
}else{
// Do this for IE
original(message)
}
counter++;
}
}
var methods = ['log', 'warn', 'error', 'count']
for (var i = 0; i < methods.length; i++)
intercept(methods[i])
}
To take Felix King's answer a bit further. Here is a more accurate way to intercept and count anything being sent to the console.
You can customize the function a bit and add any methods you want to track and count.
All console methods
I talk about it more here
I have always wondered this and every now and again it pops up.
var name = document.title;
var user = document.getElementsByClassName("class-name")[0].children[j].getElementsByTagName("a")[0].innerHTML;
var someArr = [];
for (var j = 0; j < document.getElementsByClassName("class-name")[0].children.length; j++) {
if (user == name) {
someArr.push(user)
};
};
alert(someArr);
Now this is all made up (obviously) but see how the variable "user" is checking for children[j], well if I were to try use this code, it would come up with an error along the lines of "Cannot read property 'getElementsByTagName' of undefined". Now my question is: Is there a way to allow this code to work without giving an error message and not executing. I would use this for clean code in the if loop, like all variables are used for. This wouldn't be the only instance of the "user" variable being used either so it is very useful.
Thanks in advance,
Daniel.
Your j variable will have undefined value if you use it before the for loop.
You could put it inside a function closure, but using variables like this does not make code readable in my opinion. What you can do is create a getUser() function and also cache the array of elements. Something like this:
var name = document.title;
var getUser = function(elem) {
return elem.getElementsByTagName('a')[0].innerHTML;
};
var initialArr = document.getElementsByClassName("class-name")[0].children;
var someArr = [];
for (var j = 0; j < initialArr.length; j++) {
var user = getUser(initialArr[j]);
if (user == name) {
someArr.push(user)
};
};
alert(someArr);
function createTextFields(obj) {
for (var i = 0; i < obj.length; i++) {
var dataDump = {};
for (var key in obj[i]) {
var textField = Ti.UI.createTextField(pm.combine($$.labelBrown, {
left: 200,
height:35,
value:obj[i][key],
width:550,
keyboardType:Ti.UI.KEYBOARD_NUMBER_PAD,
layout:'horizontal',
backgroundColor:'transparent',
id:i
}));
dataDump[key] = textField.value;
var callback = function (vbKey) {
return function (e) {
dataDump[vbKey] = e.source.value;
};
}(key);
}
globalData.push(dataDump);
}
}
I am using the simlar code for Adding the data and it works fine. I posted the problem yesterday and it got resolved...
Last Object is always getting updated?
Now when i go to edit page, it shows me four text fields or number of text fields added... now when i edit something and click on save... the value get's updated on the fourth or the last TextFields Object...
Don't define functions inside loops. Computationally expensive and leads to problems, like this one. Here's a fix that should solve it:
function createTextFields(obj) {
var callback = function (vbKey, localDump) {
return function (e) {
localDump[vbKey] = e.source.value;
};
}
var i;
var max = obj.length;
for (i = 0; i < max; i++) {
var dataDump = {};
for (var key in obj[i]) {
dataDump[key] = textField.value;
var callBackInstance = function(keyn, dataDump);
}
globalData.push(dataDump);
}
}
JavaScript does not have block level scope, so your variables dataDump and callback, though "declared" inside for-loops actually belong to the function. As in, you're saving a value to dataDump, then you're overwriting it, each time you go through the loop. Which is why finally only the code that operated on the last value remains.
Take a look at What is the scope of variables in JavaScript? too.