Replacing HTML will result in event handlers being unbound and replacing the entire text for an element will result in HTML markup being lost. The best approach leaves any HTML untouched, manipulating only the first text node of the matching element. To get that text node, you can use .contents()
and .filter()
:
function wrapFirstWord () {
// Select only the first text node
var node = $("div").contents().filter(function () {
return this.nodeType == 3;
}).first(),
// Get the text...
text = node.text(),
// ... and the first word
first = text.slice(0, text.indexOf(" "));
if (!node.length)
return;
// Remove the first word from the text
node[0].nodeValue = text.slice(first.length);
// Add it back in with HTML around it
node.before('<span>' + first + '</span><br/>');
};
Working demo: http://jsfiddle.net/9AXvN/
Using this method will ensure that manipulating the first word will have no unwanted side effects on the rest of the element’s content.
You can easily tack this on as an extension to jQuery, with an optional value for the number of words you want to wrap:
$.fn.wrapStart = function (numWords) {
var node = this.contents().filter(function () {
return this.nodeType == 3
}).first(),
text = node.text(),
first = text.split(" ", numWords).join(" ");
if (!node.length)
return;
node[0].nodeValue = text.slice(first.length);
node.before('<span>' + first + '</span><br/>');
};
Working demo: http://jsfiddle.net/9AXvN/1/