Table data is not appending when in a function - javascript

Whenever I write my code iteratively the program runs as it is supposed to, but when I place in a function like this it breaks.
function create_tableElements() {
Let myArticle = document.createElement(‘tr’);
Let rank = document.createElement(‘td’);
}
function assign_tableElements() {
Let count = 1
rank1 = count;
rank.textContent = rank1;
heroes_name.textContent = heroes[i].name;
}
function append_tableElements() {
myArticle.appendChild(rank);
myArticle.appendChild(heroes_name);
}
Does anyone know why this may happen? Is there a way for me to call a function within a function? I am using a for loop to loop through JSON. Now if I do not place in a function and just write the code, it will run perfectly fine. Just working on readability, and organizing my code better

There's a couple issues with the code you pasted (Let instead of let or the fancy single quotes).
I'm going to assume your phone or whatever tool you used corrected it. So let's say this is your code :
function create_tableElements() {
let myArticle = document.createElement('tr');
let rank = document.createElement('td');
}
function assign_tableElements() {
let count = 1;
rank1 = count;
rank.textContent = rank1;
heroes_name.textContent = heroes[i].name;
}
function append_tableElements() {
myArticle.appendChild(rank);
myArticle.appendChild(heroes_name);
}
Your code can't work because :
the rank variable is local to the create_tableElements function and can't be accessed by the append_tableElements function
same goes for the heroes_name function, it's local to the assign_tableElements function
You can fix this by :
either declaring these variables as global variables, outside of any function. It's not really a best practice, though.
change your function's definition so that they can access the same local variables : do you really need a function to create elements and another to append them to the DOM?
you could also use an Immediately Invoked Function Expression.
(function() {
// these variables will be visible to all the functions defined in this function, but won't be global :
let rank, myArticle, heroes_name;
function create_tableElements() {
myArticle = document.createElement('tr');
rank = document.createElement('td');
}
function assign_tableElements() {
let count = 1;
rank1 = count;
rank.textContent = rank1;
heroes_name.textContent = heroes[i].name;
}
function append_tableElements() {
myArticle.appendChild(rank);
myArticle.appendChild(heroes_name);
}
// invoking your functions :
create_tableElements();
assign_tableElements();
append_tableElements();
})();

Related

pass a function with its values "locked"

Say I have a queue class that's executing a series of functions I've already declared:
class DrawQueue{
constructor(interval){
this.sequence = [];
this.interval=interval?interval:50;
}
addFunction=(fn)=>{
this.sequence.push(fn);
//throw exception here if not a function
};
execFunctions = ()=>{
let intvl = setInterval(
()=>{
const fn = this.sequence.shift();
//clear interval & return here if not a function
fn.call();
},
this.interval
)
}
}
Now I want to pass it a series of functions that have some values calculated inside them:
//I have a single count variable here but the code I'm running is being generated by a user who might have any number of variables that are being updated
let count = 0;
let counterDiv = document.querySelector('#counter')
let dq = new DrawQueue(1000);
function startCount(){ //call when window's loaded
let countFn=(()=>
{
let innerFn= function(){
let str = (function(){
return count.toString()
})();
counterDiv.innerHTML=str;
}
//imagine that any number of count variables might be being updated somewhere in the function
count++;
dq.addFunction(innerFn);
})
while(count<10){
countFn();
}
dq.execFunctions();
}
Right now this immediately sets the counter div to 10, and then keeps setting it to 10 ten more times. But I want to assign the str variable's value before I pass the functions. So the first function I pass sets the counter to 1, then 2 and so on.
I was trying to set the let str= function(... using an iife, but that didn't work.
One solution that I know would work is to make the whole function a string and then run it with eval but I really do not want to use eval unless I absolutely have to.
Is there any other way to pass these functions with certain variables already "locked in", meaning they're assigned before the function is placed in the queue?
UPDATE: To clarify, this is just a simplified version of a more complex example. In the actual example, the code is dynamically generated by another user, so in addition to 'count' any number of other values might need to be evaluated. So passing the count variable, as several good answers have suggested, is not going to work.
FURTHER CLARIFICATION: What I'm saying above is that because the user could be generating any number of variables that will be updated as the code runs, I can't pass those variables as arguments. (imagine there might be a count2, count3...countn and I don't know how many or where they'll be used or updated in the code.
FURTHER UPDATE: so a commenter wants to see the code in which this applies so here goes. It is an application using Blockly and P5 play, where users will be making code with blocks to move a sprite. So the code for the blocks might be something like this (yes this code is really ugly because it's just a test, but you asked to see it):
function moveForward(sprite){
let dir = ship.rotation* (Math.PI / 180);
let deltaX = Math.cos(dir)*5;
let deltaY = Math.sin(dir)*5;
let newX = ship.position.x + deltaX;
let newY = ship.position.y + deltaY;
ship.position.x=newX;
ship.position.y=newY;
redraw();
}
function moveBackward(sprite){
let dir = ship.rotation* (Math.PI / 180);
let deltaX = Math.cos(dir)*5;
let deltaY = Math.sin(dir)*5;
let newX = ship.position.x - deltaX;
let newY = ship.position.y - deltaY;
ship.position.x=newX;
ship.position.y=newY;
redraw();
}
function turnLeft(sprite){
let newDir=ship.rotation-90;
ship.rotation=newDir;
redraw();
}
function turnRight(sprite){
let newDir=ship.rotation+90;
ship.rotation=newDir;
redraw();
}
There will be any number of other sprites, each with 20 or so properties that could be updated.
Now if I just put all these functions in a row, the sprite will just immediately jump to where the code would put it. Because, you know, normally we want computers to do things as fast as they can.
But since this is made for teaching, I want the user to see the canvas updating step by step, with a delay between each redraw. That means every sprite will have its x and y coordinates, along with color and rotation and a bunch of other things, change slowly.
So the purpose of the DrawQueue to execute the drawing update steps slowly with a setInterval and update the canvas at any interval I want. I can't just run every single command with a setInterval because there could be logic or loops in there. The only thing I want to go in the interval is the updates to the canvas, anything else can happen as fast as it wants.
So imagine the four functions I provided above, along with any number of other functions and modifications to the properties of any number of other sprites or properties of the canvas.
The problem you have is the value is not stored at the time you make the function. It is just a reference to a variable that you are updating. So when it calls, it is reading that variable.
You would need to pass it into the method so you can store the state of the variable at that moment in time.
let count = 0;
let counterDiv = document.querySelector('#counter')
let dq = new DrawQueue(1000);
function startCount(){ //call when window's loaded
let countFn=((count)=> . // <-- reference it here
{
let innerFn= function(){
let str = (function(){
return count.toString()
})();
counterDiv.innerHTML=str;
}
dq.addFunction(innerFn);
})
while(count<10){
countFn(count++); // <-- update it here
}
dq.execFunctions();
}
By the time the innerFn is actually called, the count variable has already increased to its final value.
To give each innerFn instance its own value for count, you could bind it as function argument:
let innerFn = function(count) { //<--- argument
let str = (function(){
return count.toString()
})();
counterDiv.innerHTML=str;
}.bind(null, count); // pass the global count into a bound argument
NB: make sure to check in your class that fn is defined (as the array will become empty at some point).
class DrawQueue{
constructor(interval){
this.sequence = [];
this.interval=interval?interval:50;
}
addFunction(fn){
this.sequence.push(fn);
//throw exception here if not a function
};
execFunctions(){
let intvl = setInterval(
()=>{
const fn = this.sequence.shift();
//clear interval & return here if not a function
if (fn) fn.call();
},
this.interval
)
}
}
let count = 0;
let counterDiv = document.querySelector('#counter')
let dq = new DrawQueue(1000);
function startCount(){ //call when window's loaded
let countFn=(()=>
{
let innerFn= function(count){
let str = (function(){
return count.toString()
})();
counterDiv.innerHTML=str;
}.bind(null, count);
count++;
dq.addFunction(innerFn);
})
while(count<10){
countFn();
}
dq.execFunctions();
}
window.onload = startCount;
<div id="counter"></div>
Even better would be to avoid a reference to a global variable, and pass count to the countFn function as parameter:
let counterDiv = document.querySelector('#counter')
let dq = new DrawQueue(1000);
function startCount(){ //call when window's loaded
let countFn=((count)=> // local variable
{
let innerFn= function(){
let str = (function(){
return count.toString()
})();
counterDiv.innerHTML=str;
}
dq.addFunction(innerFn);
})
for(let count = 0; count<10; count++){ // local variable
countFn(count); // pass it
}
dq.execFunctions();
}
Addendum
In your question's update you speak of more variables. In that case, pass an object around, which can have many properties, possibly even managed completely by the user-provided code:
let counterDiv = document.querySelector('#counter')
let dq = new DrawQueue(1000);
function startCount(){ //call when window's loaded
let countFn=((state)=> // local variable with count property
{
let innerFn= function(){
let str = (function(){
return state.count.toString()
})();
counterDiv.innerHTML=str;
}
dq.addFunction(innerFn);
})
for(let count = 0; count<10; count++){ // local variable
const state = {};
state.count = count;
countFn(state); // pass it
}
dq.execFunctions();
}
Depending on your expectations you should either use the same state object or create new state variables (within the loop, or even deeper in the execution context). This all depends on how you want the system to behave.

JavaScript arrow function to count time it gets called but does not take any parameter

I receive a challenge to create a function (arrow function) that count the times it gets called or invoked. BUT, this function can't take any parameter or interact with outside scope likes the normal way below.
Normal way:
var count = 0;
var countTimesCalled = (count) => {
count+=1;
return count;
}
console.log(countTimesCalled(count))
Is it possible to have created a function that does not take any parameter and not interact with outside scope to count the times it gets called? Where to store the times (var count) on runtime and after runtime?
Please help!
Use an IIFE to hold the count variable in a local scope:
var countTimesCalled = (() => {
var count = 0;
return () => ++count;
})();
console.log(countTimesCalled());
console.log(countTimesCalled());
console.log(countTimesCalled());
not interact with outside scope
That is not possible, but you could do something that looks kind of like it does that:
var countTimesCalled = (count => _ => ++count)(0);
console.log(countTimesCalled());
console.log(countTimesCalled());
console.log(countTimesCalled());
In reality there are two arrow functions there, an outer one to hold the count variable that is called once, and an inner one that is returned and referenced by countTimesCalled. That inner one is interacting with a scope outside of itself, but only as far out as the outer arrow function.
You could use .bind to capture state, if you really wanted to avoid accessing another closure scope entirely, e.g.
var countTimesCalled = function() {
return this.counter++
}.bind({ counter: 1 });
Set an attribute to the function, i.e: countTimesCalled.i
var countTimesCalled = () => (countTimesCalled.i = (countTimesCalled.i || 0) + 1);
console.log(countTimesCalled())
console.log(countTimesCalled())
console.log(countTimesCalled())

Recursive function doesn't return all data

I want to list only the folder names from a specific drive in Drive. So I first call the function getFolderNames() and add the parent folder name there. Then I call the getChildFolders(parent) function which gets all the sub-folders and keeps getting called recursively for every sub folder.
My problem is that the variable folderatt (which is supposed to hold all the information about the folder names) has only the names of the first folder, so only the sub-folders of the parent folder. But when I log inside the recursive function, it displays all the folder and sub-folder names correctly.
function getFolderNames() {
var folderatt = [];
try {
// If you want a tree of any sub folder
var parent = DriveApp.getFoldersByName("Librari").next();
var dhenat = getChildFolders(parent);
folderatt.push(dhenat);
} catch (e) {
Logger.log(e.toString());
}
return folderatt;
}
function getChildFolders(parent) {
folderNames = [];
var childFolders = parent.getFolders();
var thirr = 1;
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
var allFolders = {};
allFolders.emri = childFolder.getName();
allFolders.id = childFolder.getId();
folderNames.push(allFolders);
thirr++;
folderNames.push(thirr);
// Logger.log(childFolder.getName());
// Logger.log(folderNames);
// Recursive call for any sub-folders
getChildFolders(childFolder);
}
return folderNames;
}
JSFiddle - Have a look at this fiddle.
Declare and initialize your 'folderNames' variable outside the recursive function. That should solve your problem. Also, you need not to return it to 'dhenat' variable in your parent fucntion as now 'folderNames' will hold the results
There are a couple of problems I can see with your code, but first and for most would be that you don't really do anything with the result of the getChildFolders( childFolder ) call, so naturally, it doesn't get included in the result set, now possibly you could change it to this
// Recursive call for any sub-folders
folderNames.concat( getChildFolders( childFolder ) );
But you should be aware, that currently your folderNames variable is a global variable (as it is not really defined, you just assign it a value). As long as it is global, even the concat won't help you, so you also have to assign it, eg:
let folderNames = [];
The reason that it won't help would be that each time you call the getChildFolders function, the folderNames would be set to an empty array again in your example, so folderNames would only contain the results of the last time you called getChildFolders( childFolder ). By defining it locally, there won't be such a side effect
So in the end, the first function can be rewritten as:
function getChildFolders(parent) {
let folderNames = [];
let childFolders = parent.getFolders();
while (childFolders.hasNext()) {
let childFolder = childFolders.next();
let allFolders = {
emri: childFolder.getName(),
id: childFolder.getId()
};
folderNames.push(allFolders);
// Recursive call for any sub-folders
folderNames.concat( getChildFolders( childFolder ) );
}
return folderNames;
}
Please note that I removed the thirrr variable, as it was unclear to me for what purpose you need it.
In the end this should give you a flattened array that contains your allFolders object. I also changed the var to let keywords as var is a function scoped keyword, that doesn't make sense to be defined inside a block (in this case, your while loop)

Scope at let to a if statement

Lots of times I am needing to delcare a variable just for a truthy if statement.
For example:
let entry;
entry = entries.find(....);
if (entry) {
// use entry
}
// i dont need entry here
I tried combintations similar to for (let i=0; ...) like this:
if (let entry = entries.find(....)) {
// user entry
}
But it's not working. If I use var instead of let it works but the variable it hoisted so its not limited to that if statement block.
Since let creates a block scope, you need to create another block around it to limit its scope.
A block statement is used to group zero or more statements. The block is delimited by a pair of curly brackets.
let x = 1;
{
let x = 2;
}
console.log(x); // logs 1
Alternatively you can use an Immediately Invoked Function Expression:
(function () {
let entry = 6;
if (entry) {
console.log(entry);
}
})()
// Variable entry is not accessible from the outside scope
This is probably an idiom that was never made for JS, but just for kicks, here's a helper that could be used, although the other answer is probably more correct.
This was inspired by Clojure's when-let that does exactly what you're looking for, but doesn't require a function since it's a macro:
function ifDo (maybeTrueVal, doF) {
if (maybeTrueVal) {
doF(maybeTrueVal);
}
}
ifDo(entries.find(....), (truthyVal) => {
console.log(truthyVal);
});

Javascript Closures and self-executing anonymous functions

I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.
Here's the question:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.
But when I ran the below:
countFunctions[0];
countFunctions[1];
countFunctions[2];
It didn't work, with all the output being stuck at count2.
So I tried to do the below instead:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.
Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.
The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").
What you didn't notice is that setInner does, when called, return the closure function:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.
Take a look at these four functions:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
I think, first function is obvious.
In function noArguments you reference the global argument value;
The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.
So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments,
that reference global variable i.
In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.
In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.

Categories

Resources