Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrap each span in a container

Tags:

javascript

I am trying to wrap each span in a container. To achieve this I used a for loop to create multiple containers and then append the span to the container of the same index.

Why isnt my logic working?

Html: Two span tags (Sorry html code wont show)

Javascript:

var spans = document.getElementsByTagName('span'),
    body = document.getElementsByTagName('body')[0];

for(var i = 0; i < spans.length; i++)
{
      var container = document.createElement('figure');
      container.setAttribute('class', 'container');
      body.appendChild(container);
      container.appendChild(spans[i]);
} 

Edit: https://jsfiddle.net/tsrLutpg/1/

like image 682
Asperger Avatar asked Apr 26 '26 13:04

Asperger


2 Answers

Part of the reason for the oddity is because the HTMLCollection set to spans is "live." This means, as you modify a <span>, the collection changes to reflect that change.

In this case, the collection changes the order the <span>s are listed in. As you're iterating, some may be wrapped twice moving from one <figure> to another, while others may remain unaltered when they move to an index that's already been visited.

<span>Foo</span>
<span>Bar</span>
<span>Baz</span>
for (var i = 0; i < spans.length; i++)
{
    // ...
    console.log(Array.from(spans).map(s => s.outerHTML));
    container.appendChild(spans[i]);
}
// ["<span>Foo</span>", "<span>Bar</span>", "<span>Baz</span>"] (1 2 3)
// ["<span>Bar</span>", "<span>Baz</span>", "<span>Foo</span>"] (2 3 1)
// ["<span>Bar</span>", "<span>Foo</span>", "<span>Baz</span>"] (2 1 3)

You can avoid this by creating a static collection of the <span>s to iterate over, that doesn't change as the <span>s change.

In modern browsers, you can use Array.from() for this (similar to the above snippet).

var spans = Array.from(document.getElementsByTagName('span'));

https://jsfiddle.net/394La14t/


For compatibility, MDN offers a polyfill you can use. Or, you can call .slice() instead.

var spans = Array.prototype.slice.call(document.getElementsByTagName('span'), 0);
like image 65
Jonathan Lonowski Avatar answered Apr 29 '26 01:04

Jonathan Lonowski


Here is the solution that properly wraps span tags:

var spans = document.getElementsByTagName("span");
for (var i = 0; i < spans.length; i++) {
   var container = document.createElement("figure");
   container.className = "container";
   var span = spans[i];
   span = span.parentNode.replaceChild(container, span);
   container.appendChild(span);
}
like image 43
ioseb Avatar answered Apr 29 '26 01:04

ioseb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!