18
$("div.date")
    .contents()
    .filter(
        function(){
            return this.nodeType != 1; 
        })
    .wrap("<span/>");

I am new and thought that code would have done the trick, but it wraps everything in the <span> like so:

<div class='date'><span>Dec 22, 2011</span></div>

It is supposed to look like this:

<div class='date'>
  <span>Dec</span>
  <span>22,</span>
  <span>2011</span>
</div>
user982551
  • 195
  • 1
  • 1
  • 6
  • You might want to look into the [Lettering](http://letteringjs.com/) plugin. – Pointy Dec 22 '11 at 20:10
  • Try to improve your question by giving an example of what you want. A better question name seems to be 'How to wrap **each word** of an element in a span tag?' – Richard Dec 22 '11 at 20:23

10 Answers10

32

You don't need jQuery for this simple task. String.prototype.replace and regex should do the trick.

I just made some simple utility functions, that wraps letters, words and lines:

/**
 * Wraps a string around each character/letter
 *
 * @param {string} str The string to transform
 * @param {string} tmpl Template that gets interpolated
 * @returns {string} The given input as splitted by chars/letters
 */
function wrapChars(str, tmpl) {
  return str.replace(/\w/g, tmpl || "<span>$&</span>");
}

/**
 * Wraps a string around each word
 *
 * @param {string} str The string to transform
 * @param {string} tmpl Template that gets interpolated
 * @returns {string} The given input splitted by words
 */
function wrapWords(str, tmpl) {
  return str.replace(/\w+/g, tmpl || "<span>$&</span>");
}

/**
 * Wraps a string around each line
 *
 * @param {string} str The string to transform
 * @param {string} tmpl Template that gets interpolated
 * @returns {string} The given input splitted by lines
 */
function wrapLines(str, tmpl) {
  return str.replace(/.+$/gm, tmpl || "<span>$&</span>");
}

The usage is pretty simple. Just pass in the string to wrap as first argument. If you need custom markup, pass it in as the second argument, while $& is replaced by each char/word/line.

var str = "Foo isn't equal\nto bar.";
wrapChars(str); // => "<span>F</span><span>o</span><span>o</span> <span>i</span><span>s</span><span>n</span>'<span>t</span> <span>e</span><span>q</span><span>u</span><span>a</span><span>l</span> <span>t</span><span>o</span> <span>b</span><span>a</span><span>r</span>."
wrapWords(str); // => "<span>Foo</span> <span>isn</span>'<span>t</span> <span>equal</span> <span>to</span> <span>bar</span>."
wrapLines(str); // => "<span>Foo isn't equal</span> <span>to bar.</span>"
yckart
  • 32,460
  • 9
  • 122
  • 129
  • 2
    While this is a good answer, why does it wrap `isn't` as 2 different words? – Henrik Petterson Apr 25 '16 at 11:47
  • 8
    @HenrikPetterson That's because `\w` **matches any word** character (alphanumeric & underscore), and `'` is in fact not a word ;) You could use `\S` instead, which **matches any character** that is **not a whitespace** character (spaces, tabs, line breaks etc). http://regexr.com/3d9p5 – yckart Apr 25 '16 at 13:29
  • `'hello world '` not work, it cannot handle html properties well. – Arnold Feb 11 '22 at 08:26
29

It's gonna be a little more complicated than that. You're gonna have to find out all the words and re-append them to your element, wrapped in a span.

var words = $("p").text().split(" ");
$("p").empty();
$.each(words, function(i, v) {
    $("p").append($("<span>").text(v));
});

Live example

Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
16

If your element contents contains child elements (HTML) then the above solutions are not useful.

Here's a jsfiddle I've come up with that preserves HTML (elements and their attributes). The shortcoming of this small snippet is that if you have events binded to the element's contents then they will be lost since innerHTML is being reassigned to something else.

This code does not require any special libraries (like jQuery).

https://jsfiddle.net/4b5j0wjo/3/

var e = document.getElementById('words');
e.innerHTML = e.innerHTML.replace(/(^|<\/?[^>]+>|\s+)([^\s<]+)/g, '$1<span class="word">$2</span>');
ClickerMonkey
  • 1,881
  • 16
  • 13
4

I needed to give each word a specific id, so, being a newbie, I studied the previously published answers code. Starting from Brad Christie's and Daniel Tonon's code I used .addClass to achieve this result:

    $('.mydiv').each(function(){ 
    var words = $(this).text().split(/\s+/);
    var total = words.length;
    $(this).empty();
    for (index = 0; index < total; index ++){
      $(this).append($("<span /> ").addClass("myclass_" + index).text(words[index]));
      }
    })

which outputs:

    <div class="mydiv">
       <span class="myclass_0">bla</span>
       <span class="myclass_1">bla</span>
       <span class="myclass_2">bla</span>
    </div>

starting from:

    <div class="mydiv">bla bla bla</div>

It works perfectly for my needs. Maybe some expert programmers could tune up that better!

3

After a lot of research I was able to safely do it by using the Negative lookbehind feature of Regex.

htmlStr.replace(/(?<!(<\/?[^>]*|&[^;]*))([^\s<]+)/g, '$1<span class="word">$2</span>')
Muhammad
  • 6,725
  • 5
  • 47
  • 54
2

Is this what you are trying to achieve?

<span><div class="date">Dec 22, 2011</div></span>

If so:

$('div.date').wrap('<span/>');

Or are you trying to get this:

<span>Dec</span> <span>22,</span> <span>2011</span>

Something like this shoul do the trick:

var dateInner = $('div.date');
var wraps = [];
$.each(dateInner.text().split(' '), function (key, value) {
  wraps.push = '<span>' + value + '</span>';
});

dateInner.html(wraps.join(''));
mattacular
  • 1,849
  • 13
  • 17
2
var $div = $('.words');
var divWords = $div.text().split(/\s+/);
$div.empty();
$.each(divWords, function(i,w){
  $('<span/>').text(w).appendTo($div);
});

Then

<div class="words">Why hello there, world!</div>

becomes

<div class="words">
  <span>Why</span>
  <span>hello</span>
  <span>there,</span>
  <span>World!</span>
</div>
Brad Christie
  • 100,477
  • 16
  • 156
  • 200
1

If you use jQuery, try this.

Specifically, you can find an example of how to split to words here

Quote:

Here's an example of the .lettering('words') method:

<p class="word_split">Don't break my heart.</p>

<script>
$(document).ready(function() {
  $(".word_split").lettering('words');
});
</script>

Which will generate:

<p class="word_split">
  <span class="word1">Don't</span>
  <span class="word2">break</span>
  <span class="word3">my</span>
  <span class="word4">heart.</span>
</p>
Tharif
  • 13,794
  • 9
  • 55
  • 77
Gary Feng
  • 420
  • 5
  • 8
0

Just building on Xeon06 excellent answer.

I had to do this for a multiple of the same element on the same page.

    $('.element').each(function(){
        var words = $(this).text().split(" ");
        var total = words.length;
        $(this).empty();
        for (index = 0; index < total; index ++){
            $(this).append($("<span /> ").text(words[index]));
        }
    })
Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64
0

It is a simple one-line answer if you want it for each word:

str.split(' ').replace(/\w+/g,"<span>$&</span>").join(' ');
Ali Kianoor
  • 1,167
  • 9
  • 18