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)

JohnGB
  • 1,906
  • 2
  • 20
  • 31
  • 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. – David Thomas Jan 02 '12 at 15:58
  • @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. – JohnGB Jan 02 '12 at 16:09
  • What html are you working with? – David Thomas Jan 02 '12 at 16:13
  • @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. – JohnGB Jan 02 '12 at 16:23
  • Can you post your code so we can see what's going on? – James Jan 02 '12 at 16:27
  • Ok, so you are wanting the div to shrink to the text width, correct? I'll try updating my answer. – James Jan 02 '12 at 16:41
  • @JohnGB any javascript code made for this yet? – user3338098 May 05 '16 at 19:34
  • @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. – JohnGB May 06 '16 at 17:28
  • how is this still not answered correctly in 2017... – ricka May 10 '17 at 21:50
  • 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`): http://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. – johnwait Jun 17 '18 at 18:39
  • 1
    @johnwait but can you get it to work with a container that has `width: auto`? – user3338098 Jul 02 '18 at 22:19
  • 1
    Here is a fiddle which demonstrates the question, the problem, for which we still have no solution: https://jsfiddle.net/brettdonald/fm0zxpuv/1/ – Brett Donald Oct 26 '18 at 02:49
  • If I understand correctly, you'd like for the aqua box

    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://stackoverflow.com/a/37413580/18115206. I found that here: https://stackoverflow.com/q/70164816/18115206. Also, it's not ideal but have you tried text-align: justify; for

    ?

    – DystD Feb 22 '23 at 01:05

6 Answers6

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;
}
John
  • 149
  • 1
  • 2
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.

Lokipiece
  • 11
  • 1
  • Would you show an example of this in action, as it doesn't seem to answer the original question. – JohnGB Aug 23 '17 at 08:42
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.

JSG
  • 390
  • 1
  • 4
  • 13
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>
DystD
  • 99
  • 4
-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>
kwaaui
  • 7
  • 4
-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).

James
  • 3,765
  • 4
  • 48
  • 79
  • 1
    That will give me the exact situation that I have in 1a and 2a, which is the problem. – JohnGB Jan 02 '12 at 16:06
  • 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. – James Jan 02 '12 at 16:09
  • 1
    It doesn't work when the text wraps. Visually the width of the text area (not the div area) is too far left. – JohnGB Jan 02 '12 at 16:12
  • 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? – James Jan 02 '12 at 16:20
  • Can you link to the page you are testing this on so I can see what you are doing? – James Jan 02 '12 at 16:25
  • 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. – JohnGB Jan 02 '12 at 16:28
  • included in question post now. – JohnGB Jan 02 '12 at 16:40
  • 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. – JohnGB Jan 02 '12 at 17:36
  • Well without your div having another background color, it will look like centered align text unless your text wraps. – James Jan 02 '12 at 17:45