DEV Community

Cover image for Let's understand how to wrap & unwrap HTML elements
Bryan Ollendyke
Bryan Ollendyke

Posted on • Updated on

Let's understand how to wrap & unwrap HTML elements

Ever have a tag or DOM Node in Javascript and go "hmm.. I just really need that to like be in a <span> now...". Me too. We "wrap" and "unwrap" tags a lot in our projects so I decided to write three simple utilities that can help with that.

wrap

// wrap an element with another; super basic but makes it consistent across our apps
function wrap(el, wrapper) {
  if (el && el.parentNode) {
    el.parentNode.insertBefore(wrapper, el);
    wrapper.appendChild(el);
  }
}
Enter fullscreen mode Exit fullscreen mode

How it works

wrap() works by taking in a DOM node as el, the thing you want to wrap as well as a wrapper element. Think of the wrapper element as a <strong>, <span>, <div> or whatever you need to wrap something in. Then think of the el tag as the thing that you need to put inside that <strong> tag.

The function verifies that this is an element and that it has a parentNode. In JS we need to know that the element we're about to do things to has a parent, otherwise we don't know where to place something. Because the DOM is a tree structure, we can't interact with a child's placement in the page unless we know who it's parent is.

As a sentence we get code spoken like this: "Take the element to wrap, go to it's parent, then insert the wrapper juuuust before the element." This means that for a split instant we have

<parent-tag>
  <wrapper-tag></wrapper-tag>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

Lastly, we take the tag we need to wrap and run appendChild to insert it into the wrapper tag. This gives us the expected result of

<parent-tag>
  <wrapper-tag>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
  </wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

wrapAll

/**
 * Wrap an array of items all at once
 */
function wrapAll(ary, wrapper) {
  if (ary && ary.length) {
    ary[0].parentNode.insertBefore(wrapper, ary[0]);
    for (var i in ary) {
      wrapper.appendChild(ary[i]);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

How it works

The wrapAll utility takes an array of items and wraps all of them using a methodology expressed in the wrap() method above. It has a simple check that we have an actual array with values and then runs through them. Then it takes the 1st item who's parent will get our wrapper inserted like before. The difference is we'll go from this:

<parent-tag>
  <wrapper-tag></wrapper-tag>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

To this..

<parent-tag>
  <wrapper-tag>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
  </wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

unwrap

// unwrap away from an element; super basic but makes it consistent across our apps
function unwrap(el) {
  if (el && el.parentNode) {
    // move all children out of the element
    while (el.firstChild) {
      el.parentNode.insertBefore(el.firstChild, el);
    }
    // remove the empty element
    el.remove();
  }
}
Enter fullscreen mode Exit fullscreen mode

How it works

If we can wrap then we should be able to unwrap items. For this we take in an element, verify that it has a parent just like before. Then we have to look at ALL the children in this element and act upon them as it could be that we're unwrapping more than one thing. Here's what it looks like before the run it against wrapper-tag:

<parent-tag>
  <wrapper-tag>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
  </wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

We step into the wrapper tag, target the firstChild and insert it BEFORE the wrapper. As we go through and append to another item, the value of the firstChild attribute will change to be a reference to the 1st node in the wrapper. That means that in the while loop we'll have an array that keeps removing items from it and appending just before it like so:

run 1

<parent-tag>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <wrapper-tag>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
  </wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

run 2

<parent-tag>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <wrapper-tag>
    <tag-we-want-to-wrap></tag-we-want-to-wrap>
  </wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

run 3

<parent-tag>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <tag-we-want-to-wrap></tag-we-want-to-wrap>
  <wrapper-tag></wrapper-tag>
</parent-tag>
Enter fullscreen mode Exit fullscreen mode

The last part is to call el.remove() which is a built in method on all DOM Nodes to self-delete. This effectively deletes the wrapper after we've safely moved everything out of the tag and placed prior to it.

Discussion (0)