Search code examples
javascripthtmljquerycsssplit

Wrapping each letter of a string with an HTML tag with jQuery/Javascript


I need to be able to take a string of text and wrap each letter within that string with an HTML tag e.g. <i> or <span> (It doesn't matter for now).

I've got this so far, but it's not quite working as I need it to.

The issue I have is that I don't want any <span> tags currently in the string to be replaced or changed. If there is a <span> already in the string then it should keep any classes it already has or the "test_new" class should be added to the class "test" that's already there.

var words = $("p").text().split("");
$("p").empty();
$.each(words, function(i, v) {
  $("p").append($("<span class='test_new'>").text(v));
});
span {
   border: 1px solid red;
}

span.test {
    border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>This is a <span class="test">test</span> string</p>

Essentially after the code runs, the <span> should be blue meaning the <span> has retained any original classes after the string has been converted.

Any help would be much appreciated, thanks!


Solution

  • First you need to filter out to only get text nodes (node.nodeType === Node in the example below). To prevent wrapping white space (code indentation) you probably want to filter on that too (node.nodeValue.trim() !== "" in the example below).

    The filter function in jQuery allows you to do that.

    I made a function to wrap each letter of a text with your span tag using the standard map function.

    Last by looping over each text node and using the replaceWith jQuery function and our newly created wrapChars function you can wrap all characters of all text nodes.

    $('p').contents()
          .filter((i, node) => node.nodeType === Node.TEXT_NODE && node.nodeValue.trim() !== "")
          .each((i, textNode) => $(textNode).replaceWith(wrapChars($(textNode).text())));
    
    function wrapChars(text) {
      return [...text].map((c) => `<span class="test_new">${c}</span>`).join('');
    }
    span.test_new {
        border: 1px solid red;
    }
    
    span.test {
        border: 1px solid blue;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <p>This is a <span class="test">test</span> string</p>