I've made an solution that also wraps each word into a span. This way wrapping still works on whole words.
Also (1 level) of sub-elements is preserved in the end result. For example:
a <strong>bc</strong> d
Is converted into:
<span class="word">
<span class="letter">a</span>
</span>
<span class="word">
<span class="letter">
<strong>b</strong>
</span>
<span class="letter">
<strong>c</strong>
</span>
</span>
<span class="word">
<span class="letter">d</span>
</span>
Known issues:
- Only one level of sub-elements works (So
<p>Hello <strong><u>W</u>orld</strong></p>
won't)
- Text against another element create two separate words (So
<p>User<strong>name</strong></p>
are two words)
The full solution:
$.fn.convertToSeperateLetters = function() {
return this.each(function() {
var $el = $(this);
var elements = convertToSeperateLetters($el, false);
$el.empty().append(elements);
return $el;
});
}
$('p').convertToSeperateLetters();
function convertToSeperateLetters($element, asSubNode) {
var elements = [];
var childNodes = $element.contents();
// Loop through all child nodes of selected element
for (var c = 0; c < childNodes.length; c++) {
var node = childNodes[c];
var type = node.nodeType;
// Process a child element
if (type == Node.ELEMENT_NODE) {
Array.prototype.push.apply(elements, convertToSeperateLetters($(node), true));
}
// Process a piece of text
else if (type == Node.TEXT_NODE) {
var text = node.nodeValue;
// Process each word
var words = text.split(' ');
for (var w = 0; w < words.length; w++) {
var word = words[w];
// Skip empty words
if (word == '') continue;
// Wrap each word into span
var $word = $('<span/>').addClass('word');
for (var l = 0; l < word.length; l++) {
var letter = word[l];
// Wrap each letter into span
var $letter = $('<span/>').addClass('letter');
if (!asSubNode) {
$letter.html(letter);
}
if (asSubNode) {
var $subNode = $element.clone().empty().html(letter);
$letter.append($subNode);
}
$word.append($letter);
}
elements.push($word);
}
}
}
return elements;
}
p,
.word,
.letter {
padding: 3px 1px;
}
p {
border: 1px solid red;
}
.word {
display: inline-block;
border: 1px solid green;
margin-left: 2px;
margin-right: 2px;
}
.letter {
display: inline-block;
border: 1px solid blue;
}
.word-wrapped {
width: 360px;
background: lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Hello World</p>
<p>Hello <strong>World</strong></p>
<div class="word-wrapped">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
</div>