Wrap first non-HTML character with span

Wrap first non-HTML character of a paragraph with a span, which can be used to create dropcaps.

by Jon Fuller

HTML

<hr>
<p>&nbsp;"[yo]"This is a test <b>&nbsp; Other HTML <a href="#0;">tester</a> should not be affected by manipulations</b></p>
<hr>
<p>    This is a test <b>&nbsp; Other HTML <a href="#0;">tester</a> should not be affected by manipulations</b></p>
<hr>
<p><span>span tag here</span> This is a test <b>&nbsp; Other HTML <a href="#0;">tester</a> should not be affected by manipulations</b></p>
<hr>
<p><a href="#0">test</a> This is a test <b>Other HTML should not be affected by manipulations</b>
</p>
<hr>
<p>This is a test <b>Other HTML should not be affected by manipulations</b></p>
<hr>
<p>&nbsp; This is a test "Other HTML should not be affected by manipulations"
</p>
<hr>
<p><u>&nbsp;&nbsp;tester&nbsp;&nbsp;</u> This is a test "Other HTML should not be affected by manipulations"
</p>

CSS

span.fs-dropcap { color: red; }

JavaScript

(function ($) { 

	// find each instance of your target
	$('p').each(function () {
	
		// now check if the first character is "<" character
		// if is NOT a "<" character, then continue
		if ($(this).html()[0] != "<") {
			// good, now search the contents of the first TEXT_NODE within the selector
			var node = $(this).contents().filter(function () { return this.nodeType == 3 }).first(),
				// check the start of the string (which is what the ^ is for)
				// find any white space (via the "\s")
				// and chunks of contiguous whitespace, which is what the + is for on "\s+"
				// and keep finding all instances at the start, which is what the global "/g" is for
				// and convert them into nothing
				text = node.text().replace(/^\s+/g, ''),
				// now start at the beginning (0 position) and grab the first character
				first = text.slice(0, 1);

			// if the first node isn't a TEXT_NODE, then stop here
			if (!node.length)
				return;

			// remove the text character that we grabbed
			node[0].nodeValue = text.slice(first.length);
			// now add it back, before the node we checked, with a wrapper
			node.before('<span class="fs-dropcap">' + first + '</span>');

		};
		
	});
	
})(jQuery);