For loop inside my custom function doesn't work - javascript

I'm trying to create a function that uses the number input I got from users to create the same amount of division inside of a container division.However, no matter what the number is, it always creates only 1 div. It seems like the for loop inside my function is being avoided.
I've tried to alter the function, checked number input whether it is defined or undefined.
function createGrid(parameter) {
for (i = 0; i < parameter * parameter; i++); {
const div = document.createElement('div');
newDiv = container.appendChild(div);
newDiv.setAttribute('class', 'newDiv');
}
return newDiv;
}

You have semicolon ; after for loop which is essentially an empty statement.
That is the reason the for loop is not working as expected, and rest of your code is just creating one divider.
Remove the semicolon ; to fix the issue.

Additional to Nikhil's answer, here is how I would write it (without using global variables, which is considered to be bad practice in most cases):
function createGrid(parameter) {
let newDiv;
for (let i = 0; i < parameter * parameter; i++) {
newDiv = document.createElement('div');
newDiv.setAttribute('class', 'newDiv');
container.appendChild(newDiv);
}
return newDiv;
}
If you don't need to return the last div added, just remove the let newDiv; line and put the const keyword back into the first line of the for loop. Also remove the return value then.
function createGrid(parameter) {
for (let i = 0; i < parameter * parameter; i++) {
const newDiv = document.createElement('div');
newDiv.setAttribute('class', 'newDiv');
container.appendChild(newDiv);
}
}

Related

I want to get an element tag printed in the console just by clicking on it

I want to get an element tag printed in the console just by clicking on it but it doesn't seem to work and I don't get why?
can anyone point the error in my logic?
let bodyChildren = document.body.children;
let bodyArr = Object.values(bodyChildren);
for (i = 0; i < bodyChildren.length; i++) {
bodyArr[i].onclick = function () {
console.log(bodyArr[i].tagName);
};
}
The problem is that when you define a function, everything in it is contained in a separate scope. Within the function bodyArr is not known. You can use this instead to refer to the clicked element, like below:
document.body.children will only refer to the direct children of the body element. If you want to refer to every element in the DOM, you can use document.getElementsByTagName("*") instead.
When the code is written globally, like in the snippet below, the variable bodyArr is actually available in the global scope, as is the variable i. But keep in mind that the code inside the function is only executed when an element is clicked. At that point in time the for loop has been fully executed leaving i with the value 3 (since in the snippet below the script tag also counts). bodyArr will always contain exactly 1 element less, no matter how many elements are in the DOM. In this case it has 3 elements with the last element being saved at position 2 (zero based) in the array, hence bodyArr[i] equals undefined.
let bodyChildren = document.body.children;
let bodyArr = Object.values(bodyChildren);
for (i = 0; i < bodyChildren.length; i++) {
bodyArr[i].onclick = function () {
console.log(this.tagName);
}
}
<span>child1</span>
<p>child2</p>
You need to get ALL elements from the document body and this is KEY: var all = document.getElementsByTagName("*");
var all = document.getElementsByTagName("*");
let bodyArr = Object.values(all);
for (i = 0; i < bodyArr.length; i++) {
bodyArr[i].onclick = function () {
console.log(this.tagName);
};
}
<span>Hello world</span>

React - For Loop conditional render - not working and infinite trigger

I am trying to dynamically multiple an item in my render based on a variable kind of like this
<div>
<Multiple
multiple={props.multiple}
base1={props.base1}
exp1={props.exp1}
/>
</div>
const Multiple = (props) => {
let result = "";
let wtf = "";
console.log("test"); // gets triggered
for(let i = 0; i < props.multiple; i++){
console.log("i: "+i); // gets triggered
result.concat("{props.base1} +"); // this doesn't work for some reason
wtf = i; // gets triggered
}
console.log("result: "+result); // result is blank
console.log("wtf:" +wtf);
return <span>{result}</span>;
}
PROBLEM 1: Even though I am entering the for-loop, my result is not being changed and i don't understand why.
Also since I cant get it to work yet, I wanted to ask: If i do it this way, where I am concatenating {props.base1} as a string, when i return it in the render, will it show up as "{props.base1}" or will it render as the variable value?
Here is an example as to what it should look like:
base1 = abc
multiple = 2
resulting render should look like:
abc + abc +
Will concatenating my prop into a string before rendering result it in looking like this instead of the above block?
{props.base1} + {props.base1} +
PROBLEM 2: EDIT ALSO, for some reason everything in the <Multiple> component is infinitely triggering, which I also do not understand why it is happening
You are using concat, which doesn't update the original string, it creates a new string instead. What you could do is either
let result = '';
for(let i = 0; i < props.multiple; i++) {
console.log("i: "+i); // gets triggered
result += `${props.base1} `;
wtf = i; // gets triggered
}
console.log(result);
As far as the infinite loop problem goes, what actually is props.muitlple? Is it an array or a string? If so, you should change your loop to
for(let i = 0; i < props.multiple.length; i++)
Edit: if props.multiple is a number, i < props.multiple should work, you should log the value in your component and check once.
The result string is not being properly appended to
const Multiple = (props) => {
let result = "";
let wtf = "";
for(let i = 0; i < props.multiple; i++){
result += props.base1.toString() + "+"
}
console.log("result: "+result);
return <span>{result}</span>;
}
For the infinite loop I would check it's values before entering the loop to make sure your bounds are properly set.
// add this line before for loop
console.log(props.multiple)
a. change to result.concat('${props.base1} + '); (backtics!!)
b. i think that maybe there is a problem with the props you pass to <Multiple ... >. check again their value, maybe log their value.

getElementById in a for-loop only displays the first item

Relevant HTML portion
<nav>
<div class="create_button">+ Create KPI</div>
<div id="items"></div>
</nav>
Relevant JS portion
VSS.getService(VSS.ServiceIds.ExtensionData).then(function(dataService) {
// Get all document under the collection
dataService.getDocuments("MyCollection").then(function(docs) {
items = docs
for(var i = 0; i < docs.length; i++) {
console.log('doclen', docs.length)
console.log(items[i].name)
document.getElementById("items").innerHTML = "KPI Name : " + items[i].name;
}
});
});
My JS code fetches all data that I have in my VSTS storage. The docs contains an object with all items. It returns correctly and items[i].name contains the correct value that I want to display.
But this one just displays the first item in my <div id="items"> and not the rest.
Is this the right usage?
How can I fix it?
Here are 2 versions that show different ways to do this. Pay attention to the changes in the code that use es6 style.
VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService) => {
dataService.getDocuments('MyCollection').then((docs) => {
// keep a reference to the element instead of searching for it in each loop.
const itemsDiv = document.getElementById('items');
const contents = [];
for (let i = 0; i < docs.length; i++) {
// using template strings here to show you another way of working with strings in es6
contents.push(
`<div>KPI Name : ${docs[i].name}</div>`
)
}
// finally update the target element one time with your contents.
// The new line character isn't required, can just use '', but this might be easier to read for you
itemsDiv.innerHTML = contents.join('\n');
});
});
More compact version using the map functional array method. But note that this is actually slightly slower than doing a normal for loop because its executing a function on each iteration.
VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService) => {
dataService.getDocuments('MyCollection').then((docs) => {
// much more compact version using map. Note that while this is more compact,
// its slower than the for loop we did in the previous example
document.getElementById('items').innerHTML = docs.map((item) => `<div>KPI Name : ${docs[i].name}</div>`).join('\n');
});
});
The issues occours because you are setting the innerHTML of the items div on each iteration in the loop; meaning that the values will be overwritten every time and only display the last value being set in the loop.
One easy solution is to append a new element instead when you set the values to the items div
for(var i = 0; i < docs.length; i++) {
console.log('doclen', docs.length)
console.log(items[i].name)
var newElement = document.createElement('div');
newElement.innerHTML = "KPI Name : " + items[i].name;
document.getElementById("items").appendChild(newElement);
}

Using querySelectorAll to get ALL elements with that class name, not only the first

I've ditched jquery about 9(ish) months ago and needed a selector engine (without all the hassle and don't mind ie<7 support) so i made a simplified version of document.querySelectorAll by creating this function:
// "qsa" stands for: "querySelectorAll"
window.qsa = function (el) {
var result = document.querySelectorAll(el)[0];
return result;
};
This works perfectly fine for 95% of the time but I've had this problem for a while now and i have researched mdn, w3c, SO and not to forget Google :) but have not yet found the answer as to why I only get the first element with the requested class.
And I know that only the first element being returned is caused by the "[0]" at the end, but the function won't work if I remove it so I've tried to make a for loop with an index variable that increases in value depending on the length of elements with that class like this:
window.qsa = function (el) {
var result, el = document.querySelectorAll(el);
for(var i = 0; i < el.length; ++i) {
result = el[i];
}
return result;
};
Again that did not work so I tried a while loop like this:
window.qsa = function (el) {
var result, i = 0, el = document.querySelectorAll(el);
while(i < el.length) {
i++;
}
result = el[i];
return result;
};
By now I'm starting to wonder if anything works? and I'm getting very frustrated with document.querySelectorAll...
But my stubborn inner-self keeps going and I keep on failing (tiering cycle) so I know that now is REALLY the time to ask these questions :
Why is it only returning the first element with that class and not all of them?
Why does my for loop fail?
Why does my while loop fail?
And thank you because any / all help is much appreciated.
Why is it only returning the first element with that class and not all of them?
Because you explicitly get the first element off the results and return that.
Why does my for loop fail?
Because you overwrite result with a new value each time you go around the end of loop. Then you return the last thing you get.
Why does my while loop fail?
The same reason.
If you want all the elements, then you just get the result of running the function:
return document.querySelectorAll(el)
That will give you a NodeList object containing all the elements.
Now that does what you say you want, I'm going to speculate about what your real problem is (i.e. why you think it doesn't work).
You haven't shown us what you do with the result of running that function, but my guess is that you are trying to treat it like an element.
It isn't an element. It is a NodeList, which is like an array.
If you wanted to, for instance, change the background colour of an element you could do this:
element.style.backgroundColor = "red";
If you want to change the background colour of every element in a NodeList, then you have to change the background colour of each one in turn: with a loop.
for (var i = 0; i < node_list.length; i++) {
var element = node_list[i];
element.style.backgroundColor = "red";
}
You are returning a single element. You can return the array. If you want to be able to act on all elements at once, jQuery style, you can pass a callback into your function;
window.qsa = function(query, callback) {
var els = document.querySelectorAll(query);
if (typeof callback == 'function') {
for (var i = 0; i < els.length; ++i) {
callback.call(els[i], els[i], i);
}
}
return els;
};
qsa('button.change-all', function(btn) {
// You can reference the element using the first parameter
btn.addEventListener('click', function(){
qsa('p', function(p, index){
// Or you can reference the element using `this`
this.innerHTML = 'Changed ' + index;
});
});
});
qsa('button.change-second', function(btn) {
btn.addEventListener('click', function(){
var second = qsa('p')[1];
second.innerHTML = 'Changed just the second one';
});
});
<p>One</p>
<p>Two</p>
<p>Three</p>
<button class='change-all'>Change Paragraphs</button>
<button class='change-second'>Change Second Paragraph</button>
Then you can call either use the callback
qsa('P', function(){
this.innerHTML = 'test';
});
Or you can use the array that is returned
var pList = qsa('p');
var p1 = pList[0];
This loop
for(var i = 0; i < el.length; ++i) {
result = el[i];
}
overwrites your result variable every time. That's why you always get only one element.
You can use the result outside though, and iterate through it. Kinda like
var result = window.qsa(el)
for(var i = 0; i < result.length; ++i) {
var workOn = result[i];
// Do something with workOn
}

Javascript function with dynamically generated arguments

Below code :
loop(n times)
create HTML Button Element
count++;
assign onclick event = function(){
openSomething("Value_"+count)
}
so if i create 3 input elements (n=3) and then go back click any of the three buttons then every time openSomething("Value_"+3) only gets called.
why openSomething("Value_"+1) and openSomething("Value_"+2) does not get called?
I am not sure what is going on may be it the scope issue but i dont know much about scope either, any help to push me in the right direction is much appreciated.
My original code
var count = 0;
for(var i =0;i<someValue;i++){
count++;
var button = document.createElement("img");
button.src = "/images/small_button.gif";
button.imageButton = true;
button.srcBase = "/images/small_button";
button.onclick = function () {
selectSomething("someIdText_"+count);};
cell.appendChild(button);
}
Because JavaScript doesn't have block-level scoping of variables, and as a result everything is scoped to the function. That means that when you have code that uses a variable (like your loop counter n or your count variable) at a later point (i.e. after the full execution of the function), it will have its value set to the last value for the loop. You need to create a closure (a new scope for the variable) inside of your loop. Something like this (since you didn't post your actual code):
for(var i = 0, l = list.length; i < l; i++) {
(function(count) {
something.onclick = function() {
openSomething("Value_" + count);
}
})(i);
}
For a more modern approtce use let,
works for firefox, chrome, and node
if you need to target all the browsers, use Anthony approach
for(var count = 0, l = list.length; count < l; count++) {
let count;
something.onclick = function() {
openSomething("Value_" + count);
}
}

Categories