Sam Croft

A designer/developer on HTML, CSS, JavaScript and PhoneGap

Using a teeny-weeny bit of jQuery to add CSS inline backgrounds for blocks of text

  • 27 comments

Filed in: css, jQuery, plugin

For as long as I can remember I’ve always used a particular style of typography design when working with print based material. It’s nothing groundbreaking, and I’m sure you have used it as well.

It goes something like this:

Mos Eisley spaceport: You will never find a more wretched hive of scum and villainy. We must be cautious.
A print-based approach to text backgrounds. Simples.

Ah yes, ‘the coloured-background-blocks behind text effect/style’ I hear you say. Indeed.

And for as long as I’ve been using it, I’ve tried to find an easy way to replicate the style in web development. Working with static pages and small chunks of text this has normally been easy enough, but recently I was using the style in a WordPress theme and it needed to work without having to mess with snippets of HTML every time the style was required.

The problem with creating inline text element backgrounds

The problem is just that! Inline text elements. Strong, em, i or b (yes, i and b are valid HTML5 inline elements)—all cool, but they should only be used where semantically/stylistically necessary/appropriate. If we consider a block level paragraph or header tag then we have an immediate issue; a CSS applied background colour covers the entire block element, not just the text itself.

Marking up the print based example above, one might do the following:

<p id="block-demo">
	Mos Eisley spaceport: You will never find a more wretched hive of scum and villainy. We must be cautious. 
</p>
<p id="inline-demo">
	<strong>
		Mos Eisley spaceport: You will never find a more wretched hive of scum and villainy. We must be cautious. 
	</strong>
</p>

With a bit of CSS:

p {
	width: 190px;
	font: 16px sans-serif;
	color: #FFF;
}
	p#block-demo {
		background: #F00;
	}
	p#inline-demo strong {
		background: #F00;
	}

Which gives us this:

  1. A block level paragraph with a background

    Mos Eisley spaceport: You will never find a more wretched hive of scum and villainy. We must be cautious.

  2. An inline element (strong) with a background, within a block level paragraph

    Mos Eisley spaceport: You will never find a more wretched hive of scum and villainy. We must be cautious.

You can see the problem. Block level elements just fail at creating the effect. And while you can accomplish the look with inline elements, you have to use inappropriate markup; i.e. using a semantic inline element to create a style hook for an entire block element! Not good.

A simple solution for creating inline backgrounds for block level text

The technique of creating inline backgrounds for block levels of text is based on the second method outlined above. Using jQuery one can easily wrap an inline element around a given amount of block level text, using the wrapInner() function—like so:

<p>
	<strong>Han Solo:</strong> Han Solo. I'm captain of the Millennium Falcon. Chewie here tells me you're lookin' for passage to the Alderaan system? 
</p>
<p>
	<strong>Obi-Wan:</strong> Yes indeed, if it's a fast ship. 
</p>
<p>
	<strong>Han Solo:</strong> Fast ship? You've never heard of the Millennium Falcon?
</p>
<p>
	<strong>Obi-Wan:</strong> Should I have?
</p>
<p>
	<strong>Han Solo:</strong> It's the ship that made the Kessel Run in less than twelve parsecs. I've outrun Imperial starships. Not the local bulk cruisers mind you, I'm talking about the big Corellian ships now. She's fast enough for you old man. What's the cargo?
</p>

…plus a teeny-weeny bit of jQuery:

$(document).ready(function(){
	$('#text-backgrounds p').wrapInner('<span>');
});

…and a bit of CSS:

p {
	width: 190px;
	font: 16px sans-serif;
	color: #FFF;
}
	p span {
		background: #F00;
	}

…gives us:

Han Solo: Han Solo. I’m captain of the Millennium Falcon. Chewie here tells me you’re lookin’ for passage to the Alderaan system?

Obi-Wan: Yes indeed, if it’s a fast ship.

Han Solo: Fast ship? You’ve never heard of the Millennium Falcon?

Obi-Wan: Should I have?

Han Solo: It’s the ship that made the Kessel Run in less than twelve parsecs. I’ve outrun Imperial starships. Not the local bulk cruisers mind you, I’m talking about the big Corellian ships now. She’s fast enough for you old man. What’s the cargo?

What happens here is that after the document has loaded jQuery simply wraps a span tag around all of the text within the the specified paragraph tags.

Because this takes place after the document has loaded we ensure that our markup is left neat and untouched. This is progressive enhancement at its most simple level.

With an inline element in place we now have a hook to use in our CSS—just as in the above example where I used the strong element.

Using the span inline element

The most important consideration with this technique was ensuring that an appropriate inline element was used, keeping semantics in mind.

The span element is much the same as the div element, in that it intended to be used within markup to group content and has no default style properties. Unlike the div element though, span is an inline element. This makes it perfect for use here as it doesn’t disturb a document semantically, where as an element such as strong or em would carry meaning. Similarly using b or i would require overriding their default style properties.

Considering padding with the inline-backgrounds

At this point I’m sure most will have observed that the inline backgrounds do not have any padding, unlike the image example right at the start of this article. This is because of an issue that arises when an inline element has padding and spans over more than one line of text. Actually, the issue only concerns left and right padding.

This is what happens should some padding be applied to the above technique.

Han Solo: It’s the ship that made the Kessel Run in less than twelve parsecs. I’ve outrun Imperial starships. Not the local bulk cruisers mind you, I’m talking about the big Corellian ships now. She’s fast enough for you old man. What’s the cargo?

The result is that the top and bottom padding is visible on every line, as you would hope, but that the left and right padding is only visible at the start and end of the span. This is entirely correct, and not a CSS bug, but does highlight an issue where some extra steps are required to fully incorporate padding on every line.

Creating an inline-background jQuery plugin that allows for padding

In order to allow for padding, two steps are required:

  1. Markup your text with line breaks at the end of each line
  2. Use jQuery to wrap span tags around text between every line break

At this point the jQuery function becomes a little larger, at several lines, so I’ve created a plugin for using the technique.

Taking some of our previous markup and adding some line breaks:

<p>
	<strong>Han Solo:</strong> It's the ship that made<br>
	the Kessel Run in less than twelve parsecs.<br>
	I've outrun Imperial starships. Not the local<br>
	bulk cruisers mind you,<br>
	I'm talking about the big Corellian ships now.<br>
	She's fast enough for you old man. What's the cargo?
</p>

…and using the following plugin:

(function($){
	$.fn.inlinebackgrounds = function() {
		$.each(this, function(i,t) {
			var split = $(t).html().split('<br>');
			var output = '';
			$.each(split, function(i,o){
				output += '<span>'+o+'</span>';
				if (i < (split.length - 1)) {
					output += '<br>';
				}
			});
			$(t).html(output);
		});
	}
})(jQuery);

Line 3 – the plugin initially loops through each tag that has been passed to the plugin (in the above example this would be the paragraph tag).

Line 4 – next the plugin uses the JavaScript split() method to create an array of text segments, the amount of which will be determined by the number of line break tags.

Line 5 – because the plugin is going to concatenate the output html the plugin creates an empty variable to avoid a first instance of ‘undefined’.

Line 6-11 – the main part of the plugin goes through the aforementioned array of text segments and wraps each segment with our now familiar span tags. Importantly; the plugin appends a break tag to the end of each segment (after the span tag) other than the very last one. I’m sure there is a more elegant way of achieving this, by retaining the split delimiter but I couldn’t figure it out.

Line 12 – finally the plugin replaces the original input with the modified output.

The result is nice inline text backgrounds that include padding:

Han Solo: It’s the ship that made
the Kessel Run in less than twelve parsecs.
I’ve outrun Imperial starships. Not the local
bulk cruisers mind you,
I’m talking about the big Corellian ships now.
She’s fast enough for you old man. What’s the cargo?

I should quickly mention the use of line breaks. This seems like the easiest method of achieving the padding friendly version of this technique. But it goes without saying, there is a labour trade-off here in that you’re going to have to carefully consider where your line breaks are in each paragraph tag.

One final note is that, depending on your font-size, you will need to tweak and adjust line-height to ensure that your text doesn’t overlap. I’ve found using rgba() backgrounds useful here as you can see where overlapping occurs very easily.

The alternative I considered to the above plugin was using jQuery to determine the length of each line and create the span tags that way. It seemed an overly complex route to take for a simple end product. It may not even be possible, but if it is…it sounds like overkill.

About the author

I'm Sam Croft - a thirtysomething designer/developer and co-founder of Running in the Halls Ltd—a web and app development studio in Huddersfield, UK. I was educated in graphic design and now specialise in front-end web and app development; my main passion being usability and accessibility. I strongly believe web apps (vs native) are the future and love developing for mobile using the wonderful PhoneGap.

I am a massive sports fan - Formula One in particular. I live in the Pennines with my beautiful wife, Alex. Occasionally I own a large scruffy beard.

I tweet about all of my interests - you should follow me. I also have a .

  • http://www.simonrichardson.info Simon

    Good stuff :-) If you don’t want to add line-break tags at the end of every line for some reason, you could extend it to split on a new line and/or carriage return and then test that new line isn’t empty. If it’s not empty wrap it in a span.

  • Sam Croft

    Ah, great! I wasted almost all of Sunday evening trying to get it to work using carriage returns and new lines. I’ll have to check that again…

  • http://www.dotvoid.com Danne

    While not perfect it is possible to achieve the effect with pure css. The below code is a good start. The effect is achieved by using a left border with the same color as the inline span element and then shift the actual text position.

    If not for IE you would also be able to skip manually inserted BR elements and use “white-space: pre-line” instead. But it does not seem to be supported by IE.

    div#column {
    width: 500px;
    border-left: 6px solid #fe0000;
    }

    p.subtitle {
    display: inline;
    font: bold 14px/18px arial;
    color: #fff;
    background: #fe0000;
    padding: 1px 0 1px 0;
    }

    p.subtitle span {
    position: relative;
    left: -3px;
    }

    Ground round
    salami pig, meatball short loin frankfurter
    short ribs pork hamburger rump
    strip steak beef ribs T-bone salami ham hock.

  • Sam Croft

    Hi Danne,

    This looks like a really interesting solution. I’m going to try it this afternoon :-)

  • Danne

    Hi again,

    I jotted down my solution with explanations at http://www.dotvoid.com/2011/10/background-color-for-inline-text/

  • Danne

    Hi again,

    I jotted down my solution with explanations at http://www.dotvoid.com/2011/10/background-color-for-inline-text/

  • Anonymous

    This is really great, Danne. Kudos! I’ll update the post with a reference to your article.

    I understand what you’re saying about not using jQuery for style, and completely agree of course. That’s not really why I was using it though. My reason was to use jQuery to add the extra style hooks (span tags for each line) after the dom had loaded, rather than include them in the base markup and to therefore keep things as clean as possible.

  • http://twitter.com/CodeMonkeyG CodeMonkeyG

    Hi five on this solution! The desired affect is achieved without a hole lot of beating round the bush! Thanks!

  • Anonymous

    Glad you found it useful :D

  • http://twitter.com/indextwo Lawrie Malen

    This is absolutely perfect – I hadn’t considered that inline child elements wouldn’t inherit their parent’s block-level wrapping.

    I actually spent a pretty long day working this out in Flash & Actionscript 3 using textLineMetrics and was looking for an equivalent in jQuery, but this is WAY simpler. Thanks!

  • Sam Croft

    Glad it helped, Lawrie!

  • Peter

    Could you give me a hint how to get this to work in wordpress. there seems to be a problem with the
    jquery link…

  • http://samcroft.co.uk/ Sam Croft

    Do you mean a link to the jQuery library itself? In WordPress you need to make sure you create the link in your functions.php theme file, rather than in the header.php file itself.

    See this article for more information on exactly how to do this: http://css-tricks.com/snippets/wordpress/include-jquery-in-wordpress-theme/

  • Peter

    Thank you very much – i will try this and let you know about the result

  • http://samcroft.co.uk/ Sam Croft

    No problem. The reason for doing this so is not to have any conflicts if multiple instances of jQuery were loaded, by other plugins for example.

    If that still doesn’t work, open up your browser console/inspector and see what error message you’re getting and that should point you in the right direction.

  • http://twitter.com/guiiipontes Guilherme Pontes

    Thank you! But, how can I add the spacing when a manual br can’t be added though?

  • http://samcroft.co.uk/ Sam Croft

    The only other option is to check for new lines/carriage returns, but you would still need to add them in your content. 

    It should be possible to check for r instead of the br. See the first comment for more on this.

  • http://www.facebook.com/OwenMelbz Owen Melbourne

    using a similar concept we’ve come up with this variation to enable dynamic text content to gain the same effect. enjoy

    http://jsfiddle.net/OwenMelbz/rd8Qc/ 

  • drtimofey

    I can’t believe no one is talking about this. I’ve been using this for a while!

  • martijn de valk

    CSS only solution.

    Works in most modern browsers IE9+

    p {

    padding:5px 0;

    color:#fff;

    font-size: 18px;

    display: inline;

    background-color:red;

    box-shadow: 10px 0 0 0 red, -10px 0 0 0 red;

    left:10px;

    position: relative;

    }

  • http://samcroft.co.uk/ Sam Croft

    That’s awesome, Martijn. Did a quick test and seems to work really well. Thanks for sharing!

  • http://www.facebook.com/davidmarkclements David Clements

    I combined the CSS boxshadow method with the jQuery method, with a fallback for browsers that don’t support boxshadow (e.g. less than IE9).
    This also deals with WebOS browsers which give a false positive to boxshadow tests.

    You can view all of the following here: https://github.com/davidmarkclements/inlinebackgrounds

    jQuery plugin:

    (function($){

    $.fn.inlinebackgrounds = function(bsTst, resetBg) {
    //webOs gives a false positive for boxshadows
    var isWebOS = /webos/i.test(navigator.userAgent.toLowerCase());
    if (!bsTst || isWebOS) { return; }

    $.each(this, function(i,t) {
    var $t = $(t);
    if (resetBg) {
    if (typeof resetBg === ‘string’) {
    $t.addClass(resetBg);
    } else {
    $t.css({‘background-color':’transparent’})
    }

    }
    $t.wrapInner(”);
    });
    }
    })(jQuery);

    css:

    p {
    /* this will later be removed, but provides fallback for non-supporting browsers */
    background: red;

    }

    p span {
    background: red;
    display: inline;

    /* units converted to em for more responsive padding */
    -webkit-box-shadow:.25em 0 0 0 red,-.25em 0 0 0 red;
    box-shadow: .25em 0 0 0 red,-.25em 0 0 0 red;

    /* not required but gives nice spacing effect */
    line-height: 1.425em;

    position: relative;
    left: .25em;
    }

    Usage:

    $(‘p’).inlinebackgrounds(Modernizr.boxshadow);

    or:

    $(‘p’).inlinebackgrounds(Modernizr.boxshadow, true);

    or

    $(‘p’).inlinebackgrounds(Modernizr.boxshadow, ‘resetbg’);

    Last example would be accompanied with css class:

    .resetbg { background-color:transparent!important }

    If you’ve no need for Modernizr, you can simply pass in your own box shadow detection test (see http://www.sitepoint.com/detect-css3-property-browser-support/ )

    The second optional parameter will reset the background color of the passed in element (p) to transparent, if you pass a string instead of a boolean the string will be added as a class to the element. This class can likewise be used to set the bgcolor to transparent, or it could be used to emulate boxshadow or some other method.

    The reason the second parameter is optional, is if you’re using Modernizr for the test you can just do:

    .boxshadow p {
    background-color: transparent!important;
    }

    Or of course, provide an alternative.

    :)

  • http://samcroft.co.uk/ Sam Croft

    This is awesome, thanks for sharing David! I’ll have a good look at this at the weekend, looks solid.

  • http://profiles.google.com/aaron.face.taylor Aaron Taylor

    I pondered over this solution for some time — whilst its great to have I was wondering how this could work on text that gets wrapped automatically.

    Solution I found was to use JS to wrap each word in a , give that span display inline-block and pad it out. Then simply give it a minus value as margin-right to get the correct spacing between words :)

    Yeah, the markup looks naff at runtime, but who cares? Before that its nice and clean :)

    (unedited JS below — I’m in noconflict mode)

    var j = jQuery

    j(function(j) {

    function wrapWords(el, append) {

    el = el.replace(/<[^/,”);

    var words = el.split(‘ ‘);

    var output = ”

    var i = 0;

    j.each(words, function(index, value) {

    /* This whole if statement is actually now made redundant by the CSS margins below */
    if (i < words.length – 1) {

    output += '’+ value +”;

    i++;

    } else {

    output += ”+ value +”;

    i++;

    }

    });

    append.html(output);

    }

    wrapWords(j(‘#banner h1′).html(), j(‘#banner h1′));

    j(‘#banner h1′).on(‘ready’, function() {

    j(‘.title-span’).last().addClass(‘last’);

    });

    CSS

    .title-span {

    display: inline-block;

    padding: 10px 14px 10px 14px;

    background: #FA0;

    margin-right: -10px;

    }

  • Alejandro

    The problem here is that it doesn’t work properly with border-radius

  • http://dexteradams.me/ Dexter Adams

    You my dear sir…are a visionary. Thanks for this.

  • christraverse

    Has anyone used this successfully on a responsive site where line length changes? Chrome has no problem displaying it correctly, but IE adds white lines to define the spacing areas and Firefox only displays the padding at the start and end of a line, regardless of if it breaks at any point.