Grow your CSS skills. Land your dream job.

Preventing Widows in Post Titles

Published by Chris Coyier

Widows, typographically speaking, are single words that awkwardly break down onto their own line. It looks uncomfortable, and it reads uncomfortable. Article titles are especially prone to this because of the large type / short line length, and look exceptionally awkward with a widow. For example on this blog:

In the long long ago, I used to use a WordPress plugin to deal with this. It was based on Typogrify, a little project to help make stuff like this easier to deal with on the web (automatically). Today that project has grown up and it's now called WP-Typography. This plugin doesn't just fix widows, but offers all kinds of things to help improve web typography, for example, automatically wrapping your ampersands in <span class="amp"></span> so you can use the best ampersand available.

For some reason I had it in my head that I didn't like using plugins like this. I think it's because I used to have a "Save to Delicious" button. The technique this plugin uses to fix widows is to add a &nbsp; (non-breaking space) between the last two words of a chunk of text so that it is impossible to have widows. But then when someone used my Save to Delicious button, the &nbsp; would show up there right in the post title and look ugly and strange.

So when I started to get the itch to fix this problem again, I looked to JavaScript to help out instead of PHP, so that I could target the titles alone, not the WordPress function for displaying titles. (Updated August 2013, AJ Zane suggested testing to make sure title is more than one word, otherwise it gets removed.)

$("h2 a").each(function() {
  var wordArray = $(this).text().split(" ");
  if (wordArray.length > 1) {
    wordArray[wordArray.length-2] += "&nbsp;" + wordArray[wordArray.length-1];
    wordArray.pop();
    $(this).html(wordArray.join(" "));
  }
});
  1. Split the title into an array of words
  2. Join the last two words together with a non-breaking space
  3. Remove the last item of the array (now redundant)
  4. Replace the title with the array joined back together with spaces

Works great! Although now that I don't have a "Save to Delicious" button directly on my blog anymore, I think I'll revert back to using the more robust WordPress plugin to do this. I'm sure most of you would agree that using server-side technology for something like this is the smarter way to go. My theory on these social bookmarking buttons is, incidentally, that if you are already a user of a social bookmarking service like Delicious, you already know how to bookmark something. The browser tools you have available to do so offer a far nicer user experience than clicking the button on my post does anyway...

Other Techniques

David Walsh teamed up with me today and is offering up how to accomplish these widow-busting techniques with MooTools and with PHP.

Comments

  1. I say go server-side whenever you can. It’s like your article earlier this week about using Javascript for display stuff, but you can do alot more with any server side code and not worry if the user has turned off any client side scripting in their browser.

  2. GraphicGorilla
    Permalink to comment#

    We call it “weesje” which translates to orphane.

    Never really thought of investigating to avoid them. I always used the   technique too.

    Typography on the web is a tricky-tricky 1-2. If only there were more hours in a day to keep up with everything.

    Like your post !

    • Permalink to comment#

      Are you Dutch by any chance? There’s such great/strange/funny words associated with typography in Dutch. Like swearing words. My favourites are probably ‘hoerenjong’ and ‘weeskind’. Cracks me up every time.

  3. edouard
    Permalink to comment#

    great !
    added to my script, thx a lot.

  4. Permalink to comment#

    I just started reading your blog a few days ago and I added it to my Google Reader and I enjoy your tips and tricks very much. Thanks! :)

  5. I solved this problem by just adding the   non-breakable spaces right in the title field of the post editor. No plugins. No JS.
    And the submit-to-whatever URLs need URL encoded parameters and not HTML encoded parameters anyways (i.e. I convert everything in &…; to the real characters with html_entity_decode() and then use urlencode() to get the %… encoded text)

    • The first line was supposed to say “[...] problem by just adding the   non-breakable [...]

    • I don’t think I like the idea of adding it right to the title.

      1) Too much backwork if you haven’t done it from the start.

      2) What if someday you decide you DO want it to break there.

      This is a design call and should be handled externally from the content I think.

  6. Very nice tip!

  7. Excellent topic, and one I’ve never seen in a web blog. I think widows/orphans are a big deal. They can really throw things off. Nice job covering them!

  8. Permalink to comment#

    Works great! thanks

  9. Permalink to comment#

    I was going to suggest the CSS widows property, I checked my css reference and sadly, only Opera supports it. A shame! :(

  10. Permalink to comment#

    There is an easier server-side solution to this problem, that you can probable achieve by editing your WP-Typography plugin files.

    Instead of joining the last two words with a non-breaking space, just add a span around them and style the span with “whitespace: nowrap;”.

    It’s just like the ampersand fix and won’t screw up when you use a sharing service.

    • This is a possible solution… but I’m not sure if I like extra markup when a simple HTML entity does the same job.

      For anyone wanting to try this, the CSS property is actually “white-space” not “whitespace”.

    • Permalink to comment#

      I hear you, but this is how I see it:

      There are two main issues being covered here with post headlines: ampersands and widows. So we use WP-Typography which handles both of these issues on the server side.

      But then our fix creates a new problem for sharing services like Delicious. Your answer is to apply a fix for the fix. On top of that, it’s a client side fix for a server side issue.

      I’m no guru, but I’ve been doing web development long enough to know the following two best practices:

      1. Don’t apply a fix for an adverse affect of another fix. Find out what is wrong with the original fix and correct it.

      2. When possible, don’t ever count on Javascript to correct something that can be avoided all together on the server side.

      Just my two cents. Love the site and the original topics, keep it up!

  11. hi
    interesting i was wondering about this many times. and i found this article.

    i have found one more interesting thing for the same subject. its basically css solution. check out

    http://baselinecss.com/

  12. This is an interesting technique, though, it takes place after the page is loaded, so users can notice the change. I did notice how the words wrapped in the second line.

  13. Permalink to comment#

    That’s all very nice, but keep in mind that if the browser does not support javascript or has javascript disabled (like sometimes I do myself), it just won’t work.

    I can think of 2 possible solutions.

    Manually insert an nbsp on your post title, and prevent the last word from wrapping.
    Use PHP to do the search and replace, not very easy when you have a PHP template like wordpress, super easy and efficient if you write your own PHP.or
    Resize your font size so it would not wrap, or change the css for whitespace: nowrap;, which will disable wrapping completely (could brake designs.)

    Each has its own pros and cons, javascript is also possible, only keep in mind it isn’t supported by EVERYONE.

    P.S. I hope this site supports ol, because this comment will look very awkward if not :P (Chris, I advise a preview button or a list of all supported HTML) Thanks :P

    • If you disable javascript on purpose, so many things are going to be fucked up for you on the web these days that a widow in a post tile are the least of your problems.

    • Permalink to comment#

      I am with Dor in this case. In most cases I surf the web with JS disabled, not only because of security, but also page load time. I think web apps are the thing that may fuck up, if you use them alot.

  14. I love reading the posts on this blog. I did notice a slight misconception in your definition of a widow.

    A widow is a paragraph-ending line or word that falls at the top beginning of the following page or column – and is separated by the rest of the paragraph. An orphan is a line/word that appears by itself at the bottom of a page/column.

    This definition is from The Elements of Typographic Style (v. 3.1) by Robert Bringhurst on pg 43-44

    During an internship at Workbench Magazine, the art director I worked with brought this to my attention and made me realize that one-word lines aren’t all that bad.

    I do agree that it is some-what ugly to have a one word line especially in a headline, but it’s not considered a widow or orphan. I think it’s even ok to have a one-word line if the word is long enough to clear the indentation of the next paragraph and doesn’t provide too much white-space between paragraphs.

    • Thanks for the clarification. So when talking about titles, there is no proper typographic term for a word on it’s own line? I thought widow was the closest to appropriate.

    • Permalink to comment#

      I work in book production, and we call a the first line of a paragraph on its own at the end of a column/paragraph an orphan, and the last line of a paragraph at the beginning of a paragraph a widow.

      A single word on the last line of a paragraph would usually be called a paragraph widow, but (note that I am using paragraph to cover any separate unit of text, e.g. headings, list items etc.).

  15. I do like your thinking, but I also agree that it is not practical to be doing this client side. Definatly a server-side job.

  16. Permalink to comment#

    javascript solution is the best one as it’s not destructive. Instead of replacing a space with non-breaking space use span with “white-space: nowrap” style. so you don’t modify the content, just manage it.

    • DING! DING! DING!
      We have a winner!
      You can simply apply the white-space declaration to your H2 rule from the get go and not need to do any other working around. This doesn’t need the second re-worked solution either, since the data isn’t changed.

      Ironic, how a blog called css-tricks is espousing a kludgy Javascript solution when there’s a simpler CSS only alternative. ;)

    • DING DING DING!

      If you apply the white-space property to the h2 tag it won’t wrap at all and be far worse.

      What’s with the douche bag way to start a comment?

    • ConCy
      Permalink to comment#

      Laughing my ass off here.

      PS: … <strong DING DING DING ! :D

    • ehdv
      Permalink to comment#

      What’s wrong with the <nobr> html tag?

  17. My apologies, I’ll refrain from sound effects ;)

    At least use a regular expression to simplify the process


    $("h2 a").each(function() {
    //replace white space before the last "word" with a non-breaking space
    var newText = $(this).text().replace(/\s(\S+)$/," $1");
    $(this).html(newText);
    });

    You could do it on one line, but it’s less readable.

    • that would be :

      var newText = $(this).text().replace(/\s(\S+)$/,"&nbsp;$1");

    • V1
      Permalink to comment#

      Or even better..

      $(“h2 a”).each(function() {
      var self = $(this);
      self.html( self.text().replace(/\s(\S+)$/,” $1″); );
      });

      * cache your jQuery
      * don’t use variables when you don’t need them

      ;D

    • Very good fellas, thanks!

  18. Permalink to comment#

    Just recently started reading the site. Good work! Can’t wait to play with typekit.

  19. Permalink to comment#

    Nice trick! Thanks :)

  20. Permalink to comment#

    Well, and what further?

  21. Permalink to comment#

    To avoid this I would usually just have short titles and use white-space:nowrap;, and of course – leave enough space for the title.

    Additionally, you could just substitute the non-breaking space between the last two words with wrapping them in an inline element/tag that has that white-space:nowrap; I mentioned, instead of the whole title. I find it cheaper and more fail-proof, in case an user-agent happens to have JS disabled.

  22. Permalink to comment#

    Could we also throw an if statement in there to check the length of the last word? If it’s more than a certain character length don’t add the no-break space? Sometimes the last word is really long and adding a no-break space would cause an orphan instead.

    I’ve run into this problem with sites that deal with a lot of scientific terminology.

    • Ben
      Permalink to comment#

      Sure.

      Extending my example below:

      var headings = document.getElementsByTagName("h1");
      	for (var i=0;i<headings.length;i++)
      	{
      		var h1s = headings[i].innerHTML.split(" ");
              len = h1s[h1s.length-2] + h1s[h1s.length-1];
              if (len.length <= 20)
              {
              	h1s[h1s.length-2] += " " + h1s[h1s.length-1];
             		h1s.pop();
              	headings[i].innerHTML = h1s.join(" ");
              }
      	}

      It’s set to 20 characters but you could set it lower or higher for your requirements – the script won’t run if it’s higher than 20. :-)

  23. Ben
    Permalink to comment#

    Or for those who are old school and don’t require a large JS library:


    var headings = document.getElementsByTagName("h1");
    for (var i=0;i<headings.length;i++)
    {
    var h1s = headings[i].innerHTML.split(" ");
    h1s[h1s.length-2] += "&nbsp;" + h1s[h1s.length-1];
    h1s.pop();
    headings[i].innerHTML = h1s.join(" ");
    }

    This example uses h1 but you can easily substitute that for another one. :-)

  24. Greg
    Permalink to comment#

    Just a simple suggestion:
    Instead of writing this

    wordArray[wordArray.length-2] += " " + wordArray[wordArray.length-1];
    wordArray.pop();

    try this

    wordArray[wordArray.length-2] += " " + wordArray.pop();

    the pop method removes the last element AND returns it so you can do both with just the one line.

  25. Permalink to comment#

    For some reason I can’t get this to work and it’s super frustrating. I can’t seem to figure out what I’m doing wrong. Can anybody see the error of my ways? http://gigabetz.com/jquery-test.php

  26. Aaaaaand here’s the corresponding Demo in CodePen :D

    4 years after the last post, lol.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".