3
let p = document.createElement("p");
let a = document.createElement("a");

for(let i=1; i <= 5; i++) {
    p.textContent = i;      
    a.appendChild(p);
    console.log(a);
}

Results

<a><p>5</p></a>
<a><p>5</p></a>
<a><p>5</p></a>
<a><p>5</p></a>
<a><p>5</p></a>

I am struggling to understand this basic concept. I know that when I move the assignment of let p = document.createElement("p"); inside the for loop, I will get the desired result of

<a><p>1</p></a>
<a><p>2</p></a>
<a><p>3</p></a>
<a><p>4</p></a>
<a><p>5</p></a>

I for sure thought that the textContent property's value will get overwritten by i but from the first pass-through, i jumps to 5 right away.

I just want to understand the logic behind this. Thank you and any help is greatly appreciated.

Rumba
  • 181
  • 1
  • 2
  • 9

4 Answers4

4

If you leave the declaration of p outside the loop, then the element object is created only once, whereas if you put it inside the loop, a new object is created with each iteration.

The reason for what you're seeing in the first case is that p is a single object, which you are appending to a again and again. Since it is the same object, modifying it changes it everywhere.

In the second case, you have multiple distinct element objects which you are appending to a. Modifying one has no effect on the others.

EDIT: This happens because console.log actually logs after the loop as completed, as mentioned by Patrick Evans in the comments.

Also, did you notice that a always has one element, even though we call appendChild on it multiple times?

If you want to have multiple distinct p elements, but a single p object which gets incremented, then this code should work:

let p = document.createElement("p");
let a = document.createElement("a");

for(let i=1; i <= 5; i++) {
    p.textContent = i;      
    a.appendChild(p.cloneNode(true));
    console.log(a);
}
stelioslogothetis
  • 9,371
  • 3
  • 28
  • 53
  • Even with the single object, why wouldn't the current textContent have been grabbed incrementally by the console.log's? – arbuthnott Oct 29 '17 at 10:56
  • I know that. I just want to know why the result is `

    5

    ` for `i=1` all the way 'til `i=5`. I just tried it on my console to test and that's what I got.
    – Rumba Oct 29 '17 at 10:58
2

This happens because the variable p is a pointer to the dom object you created. it is not the data itself!. It passes the data by reference.

Basically, when you append it, you're appending the reference to the object and not copying (cloning) the object several times.

That's why when you change the value of the object all places referencing it will be affected.

Like you said, to fix this just put let p = document.createElement("p"); in your for loop, or alternativly use p.cloneNode(true) so it will copy (clone) your object.

MennyMez
  • 2,426
  • 18
  • 18
0

This is best understood if we go through your code step by step:

let p = document.createElement("p"); // create an element

// loop through this 5 times
for(let i=1; i <= 5; i++) {
    p.textContent = i;      
    a.appendChild(p);
    console.log(a);
}

On the first iteration i = 1 we set p.textContent to 1

On the second iteration i = 2 we set p.textContent to 2.

But wait… how is p used in the first iteration (i = 1) different to the one in the second iteration (i = 2)? They are not different, and are referring to the same p variable. In the second step you are overwriting the p.textContent you had set in your first iteration.

Instead, you should create a new p variable inside the loop, instead of outside the loop. This ensures that each p variable is unique.

Try the following instead:

// loop through this 5 times
for(let i=1; i <= 5; i++) {
    let p = document.createElement("p"); // create an element
    p.textContent = i;      
    a.appendChild(p);
    console.log(a);
}
Daniel Apt
  • 2,468
  • 1
  • 21
  • 34
-1

From what I understand, console.log is a sort of grey area between sync and async. Clearly all the console logs you are displaying show the state of things after the whole for-loop is complete, even though they are called mid-loop.

We can usually think of console.log as synchronous, because it doesn't return anything. But it is really calling a browser function that is beyond the scope of the javascript. I think that if what you are logging is "simple" enough (strings, shallow objects, etc), then what the log displays may be evaluated right away.

But if what you are logging is sufficiently complicated (like a DOM element), then the browser may just save a reference to the object which is then parsed when the browser gets around to displaying the log to us. In this case, that would be after the entire for-loop completes.

arbuthnott
  • 3,819
  • 2
  • 8
  • 21