30

I'd like to display a short piece of text that may wrap to two or three lines. It's inside a highly visual element and for styling purposes I'd like the lines to be as equal in length as possible, preferring one line over two.

Instead of this:

This is an example of a piece of text that will wrap to two

lines.

I want this:

This is an example of a piece of

text that will wrap to two lines.

But I don't want to limit the width of the text container:

This text is long enough to just barely fit in two lines. It

should not wrap to three or more lines so no width limiting.

Is there a way to do this in pure CSS or with a tolerable amount of JavaScript?

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Kaivosukeltaja
  • 15,541
  • 4
  • 40
  • 70
  • I don't think this is possible with css only. There need scripting like JavaScript/ JQuery. – ketan Jan 19 '16 at 11:52
  • How do you want to handle the case where there is a long word right on the point that it should wrap? – Ieuan Stanley Jan 19 '16 at 12:11
  • Have you tried `max-width` on the text container ? What have you tried actually ? And why don't you want to limit the text container ? – Pogrindis Jan 19 '16 at 12:11
  • I am writng a simple javascript function . . . what is your plan about long word? should it be splited? – mehari Jan 19 '16 at 12:13
  • @Pogrindis: I haven't tried much yet since the only sure way I know of doing this would involve splitting the text to `span` elements, calculating their width and manually adding line breaks where needed on screen resize. I thought I'd ask first to see if there's a simpler solution. @Bit: No need for special handling for long words. Just note that the font is variable-width so we don't know how wide they'll end up being on the screen by text alone. – Kaivosukeltaja Jan 19 '16 at 12:25

8 Answers8

23

The CSS Text Module 4 property:

 text-wrap: balance;

will do this. https://drafts.csswg.org/css-text-4/#text-wrap

There's also a package which implements a polyfill for existing browsers.

Here's a demo of balance text. [Archived copy]

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • 3
    Until that becomes a thing, there's this... https://github.com/adobe-webplatform/balance-text – doublejosh Mar 07 '19 at 07:08
  • 1
    [Status](https://github.com/Fyrd/caniuse/issues/1064#issuecomment-1420915262). [React alternative](https://github.com/shuding/react-wrap-balancer). [Svelte alternative](https://github.com/paoloricciuti/svelte-action-balancer). – M Imam Pratama Mar 11 '23 at 13:07
3

If I understand what you're asking (and if I do, the general theme of 'use justify' doesn't quite do it), I don't think you'll be able to avoid writing a fair bit of JavaScript.

The only way I can see to do what you're asking would be to compare the width of the text to the width of the container, and adjust accordingly.

Formula: Width of a single line = width of text / roundUp(width of text/width of container)

i.e. (probably needs some adjustment to prevent it cutting words in half)

var widthOfText = functionToGetWidthOfText(width); //==120
var widthOfContainer = container.width(); //==100
var containerWidth = widthOfText / ceil(widthOfText/widthOfContainer);
// containerWidth = 120 / ceil(120/100) == 120 / 2 == 60
var formattingTextContainer.width = containerWidth
// text would go inside the formattingTextContainer, which would
// give you 2 lines of equal, 60px width

However, the issue with this is, making a "functionToGetWidthOfText". After some quick searching, all I have found is this stack overflow question where the answer is to...

Wrap text in a span and use jquery width()

i.e. put it in an element and measure how wide it is...you might be able to do that off-screen somewhere/delete it after fast enough no-one sees it, but that will probably take quite a few lines of JavaScript! (I might investigate this more after work...)

The reason you can't flat-out figure out the width without rendering it is due to the various font types and sizes that it could be represented with. So if you are always using an exact font-type and font-size, you might be able to write a function that does it without rendering it.

Community
  • 1
  • 1
Azrantha
  • 419
  • 5
  • 10
  • Yes, looks like you understood my question correctly. :) Your pseudocode example looks like a reasonable starting point in case a simpler solution doesn't come up. – Kaivosukeltaja Jan 19 '16 at 13:20
1

I created an exmaple with text-align: justify and without text-align: justify.

Have a look at:

    .center {width:200px; float:left; margin-left: 20px;}
    .justify { text-align: justify; }
      
 
    <!-- 4. -->
    <section class="center">
      <p class="justify">orem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    </section>
    <section class="center">
      <p class="">orem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    </section>
osanger
  • 2,276
  • 3
  • 28
  • 35
  • Try removing everything in the first paragraph after the word *consectetur*. I'm expecting the lines to be close to equal length, but instead *consectetur* stays alone on its own line. – Kaivosukeltaja Jan 19 '16 at 12:47
  • okay now i understand your problem. It's not that easy. I think its not possible with pure CSS. The reason is that CSS needs to calculate the middle of the string to cut it in half. – osanger Jan 19 '16 at 13:05
1

Since there seems to be no ready solutions available, I wrote a relatively simple jQuery function to do what I want. Here it is in case someone else is looking for one.

It works by cloning the element we want to adjust. The clone is hidden to prevent it from blinking when it is injected to the DOM. We then make the clone narrower one pixel at a time until it has to wrap. The width right before that happens is then set to the original element and the clone element is removed.

Better ways to achieve this are still welcome, as are suggestions to improve this one. Codepen is available here: http://codepen.io/Kaivosukeltaja/pen/jWYqZN

function adjust() {
  $('.quote').each(function() {
    // Create an invisible clone of the element
    var clone = $(this).clone().css({
      visibility: 'hidden',
      width: 'auto'
    }).appendTo($(this).parent());
    
    // Get the bigger width of the two elements to be our starting point
    var goodWidth = Math.max($(this).width(), $(clone).width());
    var testedWidth = goodWidth;
    var initialHeight = $(clone).height();

    // Make the clone narrower until it wraps again, causing height to increase
    while($(clone).height() == initialHeight && testedWidth > 0) {
      goodWidth = testedWidth;
      testedWidth--;
      $(clone).width(testedWidth);
    }

    // Set original element's width to last one before wrap
    $(this).width(goodWidth);
    
    // Remove the clone element
    $(clone).remove();
  });
}

$(window).resize(adjust);
$(document).ready(function() {
  adjust();
});
body {
  background-color: #f0f0f0;
}

.holder {
  text-align: center;
  margin: 2em auto;
}
.quote {
  display: inline-block;
  border: 1px solid #c0c0c0;
  background-color: #fff;
  padding: 1em;
  font-size: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="holder">
  <div class="quote">
    If I want text to wrap, I want the lines as close to equal length as possible.
  </div>
</div>
Kaivosukeltaja
  • 15,541
  • 4
  • 40
  • 70
0

You can use a parent tag like DIV and style that with width=50%. That would made the text in more lines.

mghj
  • 27
  • 8
0

I am afraid there is no way of doing that in CSS so I just searched it and found it here split-large-string-in-n-size-chunks-in-javascript AND counting-words-in-string

I am going to take this as a challenge and write a function for it :)

try and regex the number of words and find the length of half of that then this:

function chunkString(str, length) {
  length = length/2;
  return str.match(new RegExp('.{1,' + length + '}', 'g'));
}

.....

text-align: justify. I am not sure if i got the question right but looking at your reputation I am scared to answer this!! :)

put the text in a p, or span, make sure it is display: block, inline-block will do the job as well. width of the each element should be 50% of the parent element. then text-align: justify the child element.

Community
  • 1
  • 1
Daniel PurPur
  • 519
  • 3
  • 13
  • I am thinking to provide a javascript function that will wrap this ... but #looking at his reputation am scared too. – mehari Jan 19 '16 at 11:46
  • hahahah, same here but this is such a simple thing in css that I wonder if I got the question right! :) text justify has been around for donkey's years! – Daniel PurPur Jan 19 '16 at 11:50
  • 1
    There is no point in being intimidated by a reputation, nobody knows everything. – Pogrindis Jan 19 '16 at 12:13
  • Exactly, there's zero need to be intimidated by reputation. I've seen people with way over 10k reputation ask very basic questions on topics they're not previously familiar with. Unfortunately I can't limit the text width, see my updated third example to see why. – Kaivosukeltaja Jan 19 '16 at 12:19
0

You can put the text into a div and use both width and text-align css properties:

#center{
  width : 100px;
  text-align : justify;
}
<div id="center">This is an example of a piece of text that will wrap to two lines.This is an example of a piece of text that will wrap to two
lines.</div>
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Don D
  • 726
  • 1
  • 9
  • 19
-1

Just shorten the width of the element holding this text:

div {
    width: 210px;
}
<div>This is an example of a piece of text that will wrap to two lines.</div>
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61