bart
bart

Reputation: 15298

How can I avoid one word on the last line with CSS?

Suppose a text shows up as follows on an HTML page (just one word too long to fit on one line):

Lorem ipsum dolores amet foo
bar

How can one avoid with CSS that the last word appears on the last line, and force two (or more)?

Lorem ipsum dolores amet
foo bar

Upvotes: 21

Views: 11946

Answers (7)

Bramus
Bramus

Reputation: 2059

This question has been answered at https://stackoverflow.com/a/76701845/2076595. Nowadays, after many years of waiting, you can do this with CSS: text-wrap: pretty.

I initially added that answer to this question here as well, but a moderator deleted my answer saying I should flag questions as duplicates instead. All good, but unfortunately I don’t have enough points to cast duplicate-votes so here we are: me referring to the other answer. Sorry about that, but apparently that’s the rules.

For completeness: this questions is the OG question. These two other questions are duplicates and should get marked as duplicates of this question:

Upvotes: 2

jacob
jacob

Reputation: 365

here's some newbie php that you might use if you wanted to do this in wordpress

function add_nobr_spans($string) {
  $words = explode(' ', $string);
  if (count($words) < 3){
    return $string;
  }
  $last_word = array_pop($words);
  while ($last_word == "") {
    $last_word = array_pop($words);
  }
  if (preg_match('#[<>]+#is', $last_word)) {
    return $string;
  }
  $almost_last_word = array_pop($words);
  if (preg_match('#[<>]+#is', $almost_last_word)) {
    return $string;
  }
  array_push($words, "<span class='nobr'>".$almost_last_word." ".$last_word."</span>");
  return implode(' ', $words);
}
function no_widows_for_end_tag($input, $end_tag){
  $split_stuff = explode($end_tag, $input);
  $split_stuff = array_map('add_nobr_spans', $split_stuff);
  $result = implode($end_tag, $split_stuff);
  return $result;
}
function no_widows($string) {
  $string = no_widows_for_end_tag($string, "</p>");
  $string = no_widows_for_end_tag($string, "</h3>");
  $string = no_widows_for_end_tag($string, "</h4>");
  $string = no_widows_for_end_tag($string, "</h5>");
  return $string;
}

Upvotes: 0

K3---rnc
K3---rnc

Reputation: 7049

If JavaScript is an option, one can use typogr.js, a JavaScript "typogrify" implementation. This particular filter is called Widont.

<script src="https://cdnjs.cloudflare.com/ajax/libs/typogr/0.6.7/typogr.min.js"></script>
<script>
document.body.innerHTML = typogr.widont(document.body.innerHTML);
</script>
</body>

Upvotes: 1

Adam Grant
Adam Grant

Reputation: 13105

Just wrote this dependency-free JS snippet that will solve this very problem

https://github.com/ajkochanowicz/BuddySystem

Essentially this is the source code

var buddySystem=function(e){var n=[],r=[]
n=e.length?e:n.concat(e),Array.prototype.map.call(n,function(e){var n=String(e.innerHTML)
n=n.replace(/\s+/g," ").replace(/^\s|\s$/g,""),r.push(n?e.innerHTML=n.replace(new RegExp("((?:[^ ]* ){"+((n.match(/\s/g)||0).length-1)+"}[^ ]*) "),"$1&nbsp;"):void 0)})}

and you can implement it by doing this

objs = document.getElementsByClassName('corrected');
buddySystem(objs);

Now you'll never have a word by itself for any tags with the corrected class.

You can also use jQuery if you want.

$(".corrected").buddySystem()

Check out the link for all possibilities.

Upvotes: 6

Jack Vial
Jack Vial

Reputation: 2474

I don't think it can be done in CSS alone but this is what I came up with to solve the one word per line problem. It will replace the last n spaces with a non breaking space for all the matched elements.

https://jsfiddle.net/jackvial/19e3pm6e/2/

    function noMoreLonelyWords(selector, numWords){

      // Get array of all the selected elements
      var elems = document.querySelectorAll(selector);
      var i;
      for(i = 0; i < elems.length; ++i){

        // Split the text content of each element into an array
        var textArray = elems[i].innerText.split(" ");

        // Remove the last n words and join them with a none breaking space
        var lastWords = textArray.splice(-numWords, numWords).join("&nbsp;");

        // Join it all back together and replace the existing
        // text with the new text
        var textMinusLastWords = textArray.join(" ");
        elems[i].innerHTML = textMinusLastWords + " " +  lastWords;
      }
    }

    // Goodbye lonely words
    noMoreLonelyWords("p", 3);

Upvotes: 10

Phil Powell
Phil Powell

Reputation: 424

Can't be done with CSS, but Shaun Inman wrote a very useful bit of Javascript to help with this a while ago:

http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin

It's a Wordpress plugin, but there are plenty of non-Wordpress clones around.

Upvotes: 2

Pekka
Pekka

Reputation: 449485

I don't think you can do this in pure CSS.

You would have to either put a non-breaking space in between the last two words:

foo&nbsp;bar

or put the last two words into a span:

<span style="white-space: nowrap">foo bar</span>

Upvotes: 27

Related Questions