简体   繁体   中英

Unable to appendchild text to element

I have the following code:

 myText = document.getElementById("myText") texts = myText.getElementsByTagName("text"); let div = document.createElement("div"); console.log("Texts length: " + texts.length); for (let j = 1; j < texts.length; j++) { div.appendChild(texts[j]); } div.style.display = "none"; myText.appendChild(div);
 <div id ="myText"> <text text-anchor="middle" x="193.25" y="-178.3" font-family="Times,serif" font-size="14.00">Running</text> <text text-anchor="middle" x="75.5" y="-152.8" font-family="Times,serif" font-size="14.00"> &lt;onentry &gt; </text> <text text-anchor="middle" x="75.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;log expr=&#39; &#39;Running!&#39;&#39; &#160;&gt;</text> <text text-anchor="middle" x="75.5" y="-106.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;/log&gt;</text> <text text-anchor="middle" x="75.5" y="-75.8" font-family="Times,serif" font-size="14.00">&lt;/onentry&gt;</text> </div>

When the function ends, I have the following result.

 <div id="myText"> <text text-anchor="middle" x="193.25" y="-178.3" font-family="Times,serif" font-size="14.00">Running</text> <text text-anchor="middle" x="75.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &nbsp;&lt;log expr=' 'Running!'' &nbsp;&gt;</text> <text text-anchor="middle" x="75.5" y="-75.8" font-family="Times,serif" font-size="14.00">&lt;/onentry&gt;</text> <div style="display: none;"> <text text-anchor="middle" x="75.5" y="-152.8" font-family="Times,serif" font-size="14.00"> &lt;onentry &gt; </text><text text-anchor="middle" x="75.5" y="-106.8" font-family="Times,serif" font-size="14.00"> &nbsp;&lt;/log&gt;</text></div> </div>

Two out of the four text elements were not properly inserted into the new div. I am not sure why, can anyone help?

@Pilchard's answer is a direct response to your question. This, however, is a method of sidestepping the issue by using querySelectorAll which returns a static NodeList instead of a live HTMLCollection.

When you simplify a bit and use a forEach loop, you eliminate the need for a for loop and cuts the code down a bit. I am populating a new div with the data for demonstration

 myText = document.getElementById("newText") texts = document.querySelectorAll("text"); let div = document.createElement("div"); texts.forEach(el => div.appendChild(el)); document.getElementById("myText").style.display = "none"; myText.appendChild(div);
 text { display: block; }
 <div id="myText"> <text text-anchor="middle" x="193.25" y="-178.3" font-family="Times,serif" font-size="14.00">Running</text> <text text-anchor="middle" x="75.5" y="-152.8" font-family="Times,serif" font-size="14.00"> &lt;onentry &gt; </text> <text text-anchor="middle" x="75.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;log expr=&#39; &#39;Running!&#39;&#39; &#160;&gt;</text> <text text-anchor="middle" x="75.5" y="-106.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;/log&gt;</text> <text text-anchor="middle" x="75.5" y="-75.8" font-family="Times,serif" font-size="14.00">&lt;/onentry&gt;</text> </div> <div id="newText"> </div>

There are a number of things going wrong in your code.

First of all, you need to declare your variables.

const myText = ...
const texts = ...

Next, getElementsByTagName() returns a liveHTMLCollection which mutates to match changes to the DOM. This means that as you move elements to the new <div> you alter the length of the queried collection causing your loop to end before it's had a chance to iterate the entire collection.

If you move your console.log() inside the loop you'll see the length is one shorter on each iteration.

 const myText = document.getElementById("myText"); const texts = myText.getElementsByTagName("text"); const div = document.createElement("div"); for (let j = 1; j < texts.length; j++) { console.log(`Texts length: ${texts.length}, j: ${j}`); div.appendChild(texts[j]); } console.log(`Texts length: ${texts.length}`);
 <div id ="myText"> <text text-anchor="middle" x="193.25" y="-178.3" font-family="Times,serif" font-size="14.00">Running</text> <text text-anchor="middle" x="75.5" y="-152.8" font-family="Times,serif" font-size="14.00"> &lt;onentry &gt; </text> <text text-anchor="middle" x="75.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;log expr=&#39; &#39;Running!&#39;&#39; &#160;&gt;</text> <text text-anchor="middle" x="75.5" y="-106.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;/log&gt;</text> <text text-anchor="middle" x="75.5" y="-75.8" font-family="Times,serif" font-size="14.00">&lt;/onentry&gt;</text></div>

You can avoid this by converting the returned HTMLCollection to an array, for example with spread syntax .

const texts = [...myText.getElementsByTagName("text")];

Lastly, you're starting your loop at 1 which skips the first element because arrays (and array-likes like HTMLCollections) are zero indexed in javascript.

Working final snippet

 const myText = document.getElementById("myText"); const texts = [...myText.getElementsByTagName("text")]; const div = document.createElement("div"); for (let j = 0; j < texts.length; j++) { div.appendChild(texts[j]); } div.className='new'; myText.appendChild(div);
 .new { background-color: aqua; border: 1px solid darkgray; }
 <div id ="myText"> <text text-anchor="middle" x="193.25" y="-178.3" font-family="Times,serif" font-size="14.00">Running</text> <text text-anchor="middle" x="75.5" y="-152.8" font-family="Times,serif" font-size="14.00"> &lt;onentry &gt; </text> <text text-anchor="middle" x="75.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;log expr=&#39; &#39;Running!&#39;&#39; &#160;&gt;</text> <text text-anchor="middle" x="75.5" y="-106.8" font-family="Times,serif" font-size="14.00"> &#160;&lt;/log&gt;</text> <text text-anchor="middle" x="75.5" y="-75.8" font-family="Times,serif" font-size="14.00">&lt;/onentry&gt;</text> </div>

All that being said, I'm not sure what you're doing with your <text> elements but it's outside the scope of you question so I'll leave that to you.

I think I figured it out. Basically append child also removes the element from its previous position.

Thereby changing length and the element at index j, a better approach would be to store length and then get first element since the list becomes shorter

 length = texts.length; for(let j = 1; j < length; j++){ div.appendChild(texts[1]); }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM