How can I centre left aligned text even when it wraps?
Asked Answered
D

6

35

I need to centre a block of left aligned text within a div, but I need the visual width of the text block to be centred not the block itself.

In many cases this may be the same thing, but think of a case where the div is fairly narrow (think mobile widths) and the text is too long to fit on one line, so it needs to overflow.

In the examples below, I am showing the text block as light blue to illustrate, but in practice they will be the same colour as the parent div (white). There are also no line breaks in any text used.

enter image description here

In the 1a, the text is only one line and it is smaller than the maximum width of the text block, so I can set the text block to the width of the text and there isn't a problem.

In 2a however, the text is longer than the maximum width and so wraps to the next line. The effect of this is that the visible text block doesn't appear centred any more.

How can I display both of these situations as 1b and 2b only using HTML and CSS?

Edit 1: It seems that everyone is telling me how to achieve the situation in 1a and 2a, but I already have that. I want to achieve the situation in 1b and 2b.

Edit 2: The code I'm using is essentially the same as what David gave in his link (http://jsfiddle.net/davidThomas/28aef/). The use of a colour for the text area is just to illustrate this point though. If you change that to white (http://jsfiddle.net/28aef/2/) you can see how the text block no longer looks centred (i.e. left and right margins aren't equal)

Dreamland answered 2/1, 2012 at 15:54 Comment(13)
So...left-aligned text within a centered container? This might be difficult with just html and css, given that you appear to want the text vertically-centered as well.Onfre
@DavidThomas: If you know of a simple way of doing this in JS I'd consider it a backup option, but I'd like to avoid it if possible.Dreamland
What html are you working with?Onfre
@DavidThomas: I don't understand the question. The method that I have now is pretty much what everyone has suggested so far, but that method no longer looks centred when the text wraps and the div is fairly narrow.Dreamland
Can you post your code so we can see what's going on?Lagas
Ok, so you are wanting the div to shrink to the text width, correct? I'll try updating my answer.Lagas
@Dreamland any javascript code made for this yet?Goer
@user3338098: Unfortunately not. So far there doesn't seem to be a solution without resorting to calculating the line break position and essentially calculating the layout yourself.Dreamland
how is this still not answered correctly in 2017...Blague
This is my first (and best) attempt at a complete solution, using three nested elements and the flex-wrap CSS property (paired with display: flex): jsfiddle.net/jonathan_rb/e7uspa2k Then the only thing missing would be to have the text vertically centered when the height of the inner-most container (blue background) is more than what's needed for the wrapped text.Torero
@Torero but can you get it to work with a container that has width: auto?Goer
Here is a fiddle which demonstrates the question, the problem, for which we still have no solution: jsfiddle.net/brettdonald/fm0zxpuv/1Acquah
If I understand correctly, you'd like for the aqua box <p> to resize according to the wrapped text? You will need JS or CSS media queries according to this answer (and explanation of why it happens) https://mcmap.net/q/196853/-make-container-shrink-to-fit-child-elements-as-they-wrap. I found that here: https://mcmap.net/q/196852/-how-to-make-the-width-fit-to-the-content-when-text-wrapped-duplicate/18115206. Also, it's not ideal but have you tried text-align: justify; for <p>?Jaguarundi
I
10

I know this is old, but I just had the same issue, and I agree that none of these solve it. This one may not be 100% cross-browser (haven't done testing) but it works!

Now the restriction is you have to put in the line-break yourself, but it seems that is what needs to be done regardless in this kind of situation.

http://jsfiddle.net/28aef/2/

div.textContainer {
    text-align: center;
    border: 1px solid #000;
    height: 100px;
    padding: 10px 0;
}

div.textContainer p {
    display: inline-block;
    text-align: left;
}
Iodism answered 20/2, 2013 at 21:54 Comment(2)
This is showing left aligned text with a border on Chrome.Dreamland
Still it does not centers exactly when the text wraps - which is the main issue here.Larissa
J
1

I'm guessing this has long since become a non-issue for the original poster but I had the same question and was googling to find an answer. Below is the result of what I found.

Disclaimer: I'm not an expert at any of this and others may have a more elegant way to handle this situation but it worked for my application and may work for other applications so I figured I'd post it.

<div style = "text-align: center; margin-left: 10%; margin-right: 10%">
    <div style = "display: inline-block; text-align: left;">
        Desired text goes here.
    </div>
</div>

Note that in the above the first div may not be needed and the margins can be moved to the second div with little change. Tailor, as needed, on the percentages or specify a pixel width and you should be all set. Hopefully this saves someone some time at some point and doesn't create any new issues. Best of luck all.

Jackleg answered 22/8, 2017 at 15:4 Comment(1)
Would you show an example of this in action, as it doesn't seem to answer the original question.Dreamland
J
0

I know this is old but it has not been answered. It can be done with calc() vh and vw(viewport units), at least, and some math. The trick seems to be setting the left and top to center manually somewhat. I used viewport units for their accuracy and ever changing effect.

    .clr	{ clear:both; }
    .bg666	{ background:#666;}  
    .bgf4	{ background: #f4f4f4; }
    .hv60	{ height: 60vh; }
    .hv50	{ height: 50vh; }
    .wv60	{ width: 60vw; }
    .wv50	{ width: 50vw; }
    .tlset	{ position:relative; left: 30px; top: 30px; }
    .true_cntr_inner { position:relative; left: calc(60vw - 50vw - 5vw);  top: calc(60vh - 50vh - 5vh); } /* left = (60% - 50% of inner block / 2);*/
    <div class = 'clr wv60 hv60 bg666 tlset'>
        <div class = 'clr wv50 hv50 true_cntr_inner bgf4'>
            this is some text that spans acrossed 50% of the viewport. 
            I state the obvious because I believe people can see the obvious and I 
            was wrong a lot. Now it has become somewhat of a habit like bad spelling 
            so forgive me. 
        </div>
    </div>

I was looking for a solution to center and resize my text containing DIV on window resize. I ran across this post and found my workaround after realizing there was an issue using the regular CENTER method. I hope it helps.

Jaenicke answered 22/4, 2018 at 8:32 Comment(0)
J
0

Expanding upon the workaround I commented on the question, I found two more on the same less-than-ideal line, so I thought I'd put them together, along with a visual comparison, in case it helps someone. The only advantages are that they don't require JS and work with wrapped text (i.e., without manually adding new lines).

  1. text-align: text-align: justify; — may leave ugly spaces between words, more often in short texts with long words.
  2. hyphens: -webkit-hyphens: auto; -moz-hyphens: auto; -ms-hyphens: auto; hyphens: auto; — doesn't really fill all of the pseudo-right-margin
  3. word-break: word-break: break-all; — centered but breaks words as soon as it touches the edge
  4. A combination of the previous options.

For words that'd overflow the container, we need to add overflow-wrap: break-word;. The reason why I left that commented out was to show the difference in how each example treats long "syllables". Note that it doesn't add a hyphen (similar to word-break: break-all;).


  • Part of these workarounds were adapted from justmarkup.com website.

  • If adding manual <br> is an option, then John's answer is likely more appropriate.

  • If JS is also an option, go to my comment above and then follow the link, or not.

  • I'd also mentioned CSS media queries as they're proposed as a solution along with the JS one. However, I looked into them and I don't believe they'd work in this case as they require having content with a predictable size in order to be able to calculate the size of the container beforehand. All it does is check the size of the veiwport (visible area) so that when it falls into (or out of) a preset range the container is set to one of the predefined sizes. But in our case, we can't predict the optimal container size for any random text.

  • Container queries don't solve the problem either as they don't allow us to check the width of the text, just of the box containing it. If we set display: contents to prevent the box from being created, then the width will be 0px.

  • The issue is mentioned on a recent Chrome blog entry about balanced text. I'm quoting it because they're aware of it and I believe the same technique could be applied in order to fix it:

. . .

Balanced text wrapping ironically creates imbalance to the contained element.

A brief explanation of the technique the browser is using

The browser effectively performs a binary search for the smallest width which doesn't cause any additional lines, stopping at one CSS pixel (not display pixel). To further minimize steps in the binary search the browser starts with 80% of the average line width.

div {
  width: 10em;
  margin: 0 auto 1em auto;
  border: 1px solid #000;
  padding: 1em;
  background-color: lightgreen;
  /* overwritten in case 0 */
  
}

.text0 {
  text-align: justify;
}

.text1 {
  -webkit-hyphens: auto;
  -moz-hyphens: auto;
  -ms-hyphens: auto;
  hyphens: auto;
}

.text2 {
  word-break: break-all;
}

h1 {
  text-align: center;
}

p {
  background-color: aqua;
  /*overflow-wrap: break-word;*/
}
<!DOCTYPE html>
<html lang="en">
<!-- !DOCTYPE and html tags are required for the hyphens to work in this example -->

<body>
  <h1>Justify</h1>
  <div class="text0">
    <p>This is a bit of text displaying on more than two lines. Try to bbbbbbbbbbbbbbbbbbbbbbbreak this word.</p>
  </div>

  <h1>Hyphens</h1>
  <div class="text1">
    <p>This is a bit of text displaying on more than two lines. Try to bbbbbbbbbbbbbbbbbbbbbbbreak this word.</p>
  </div>

  <h1>break-all</h1>
  <div class="text2">
    <p>This is a bit of text displaying on more than two lines. Try to bbbbbbbbbbbbbbbbbbbbbbbreak this word.</p>
  </div>
  
  <h1>Justify and break-all</h1>
  <div class="text0 text2">
    <p>This is a bit of text displaying on more than two lines. Try to bbbbbbbbbbbbbbbbbbbbbbbreak this word.</p>
  </div>
  
  <h1>Justify and hyphens</h1>
  <div class="text0 text1">
    <p>This is a bit of text displaying on more than two lines. Try to bbbbbbbbbbbbbbbbbbbbbbbreak this word.</p>
  </div>
</body>

</html>
Jaguarundi answered 15/4, 2023 at 22:46 Comment(0)
M
-1

Flexbox may be good to go:

  .d-flex {
      display: -ms-flexbox;
      display: flex;
  }
  .flex-column {
      -ms-flex-direction: column;
      flex-direction: column;
  }
  .ml-auto, .mx-auto {
      margin-left: auto;
  }
  mr-auto, .mx-auto {
      margin-right: auto;
  }
  .w-50 {
      width: 50%;
  }
<div class="d-flex flex-column mx-auto w-50">
  <p>Left aligned text</p>
  <p><em>Aenean sed lectus massa, quis fringilla magna. Morbiporta sapien quis ligula viverra condimentum non vel nunc. Ut ac pulvinar nibh. Sed vitae eros et purus ullamcorper fermentum eu a libero. Curabitur vitae dolor ligula, varius tincidunt arcu. Quisque lectus magna, semper sed tincidunt nec, mattis vel justo.</em></p>
  <p>Using <a href="https://getbootstrap.com/docs/4.1/utilities/flex/">Bootstrap 4</a> classes.</p>
</div>
Magdau answered 21/9, 2018 at 3:31 Comment(0)
L
-3
<div style="text-align:center;">
    <div style="display:inline; text-align:left; margin:20px;"> <!-- or whatever width you want that block to be at -->
          Content here
     </div>
</div>

Basically, the first div is your containing element. You need to text-align center.

Then, in your block div (the blue area), you need to display inline and text align left, then set a margin (the white space you want to remain around it when the text wraps).

Lagas answered 2/1, 2012 at 16:2 Comment(9)
That will give me the exact situation that I have in 1a and 2a, which is the problem.Dreamland
No, as long as you set this width to the width you do not want the div to exceed, you will be fine. The div will look like 2b.Lagas
It doesn't work when the text wraps. Visually the width of the text area (not the div area) is too far left.Dreamland
Do you mean that the text is going outside of the div? Or do you mean you need padding inside of the div so that it's not right up against the edge?Lagas
Can you link to the page you are testing this on so I can see what you are doing?Lagas
Neither. When the text wraps to the next line, there is visually some whitespace between the end of the text in the first line and the edge of the text area (as with all wrapping). This whitespace makes the text block look like it is left aligned (2a) rather than centred and left aligned (2b). I want it to look like it does in 2b.Dreamland
included in question post now.Dreamland
Nope, it just shows as centre aligned text to me. From what I can see so far it seems that there isn't a way of achieving what I want without some form of JS to modify the width of the text area.Dreamland
Well without your div having another background color, it will look like centered align text unless your text wraps.Lagas

© 2022 - 2024 — McMap. All rights reserved.