13

Disclaimer: I don't believe this is a duplicate as I'm using relative sizes to produce a full screen grid layout without using px.

Problem: In this jsFiddle http://jsfiddle.net/X3ZDy/73/ I have four equally proportioned boxes. They are designed to span the screen width and remain square. Contained within them are some sample square DIVs (40% x 40%). I'm struggling though to get a text label lbl horizontally and vertically centered within bbl.

All the examples I've seen (and tried) don't work as they require me to know the height of my label, or they use browser restricted table-layout tricks. I need to do this with all relative sizes as per the fiddle.

Can anyone assist? I need to this to work on ALL browsers with a pure CSS (no JS) solution. I'm astonished that it appears to be quite so tricky to vertically align text in a div. I don't mind if we use block or inline elements as the text label.

Please note that I'm NOT looking for a TABLE solution, this is a DIV & CSS puzzle that requires a working jsFiddle.

More: Thanks all for your answers, but for future posters, note that (width == padding-bottom) is the magic that allows my DIVs to be square. It's key to a grid-layout system so I need to maintain that.

updated It's pretty tricky working with relative sizes and no fixed heights, but I think I've finally found an answer to the problem (below).

cirrus
  • 5,624
  • 8
  • 44
  • 62
  • are you trying to center the boxes or the text inside the boxes? – kinakuta Dec 21 '12 at 17:31
  • the text inside the boxes. The boxes themselves could be positioned anywhere in each of the four containers. – cirrus Dec 21 '12 at 17:36
  • The lbl divs tou are talking about I suppose – Jimmy Kane Jan 02 '13 at 13:16
  • 1
    For a bounty award, I'm looking to see a working fiddle, that maintains my relatively sized grid squares (that being the key issue) and my browser hacks necessary to make it work everywhere. So we can't have a solution with fixed sizes for instance. – cirrus Jan 02 '13 at 16:30
  • I think the crazy code formatting is down to the JSFiddle interface it seems when you tab a line, the line above actually tabs – Zeddy Jan 03 '13 at 07:19
  • how long text supposed to be aligned? something like this http://jsfiddle.net/X3ZDy/42/ makes the trick but I am afraid it is not what you are looking for – dmi3y Jan 05 '13 at 17:35
  • @dmi3y yes. Your one liner is correct, but the tricky bit is getting the multi-line to vertically center. – cirrus Jan 07 '13 at 10:08
  • Can you provide rough screen shot what you exactly want's ? i think it might helpful to us. – John Peter Jan 08 '13 at 11:59
  • @JohnPeter basically as the fiddle. Boxes should be square. Text should be vertically centered, whether it's one line or 20 lines. It would of course be clipped by the overflow if there's too much text. Vertical centering means that the middle of the text should be in the middle of the box. So if the bottom overflows, then the top would also be in negative overflow. You'd need to resize the window, to resize the boxes, to test the centering. – cirrus Jan 08 '13 at 13:48
  • Maybe you can use a new Box model: http://jsfiddle.net/X3ZDy/81/ btw, why you can't use javascript? – karacas Jan 08 '13 at 20:06

8 Answers8

17

I think I finally found an answer to the problem that works. The issue is that almost every other solution I've seen can't cope when the child size changes and none of the heights are known. I needed this to work for a responsive all % design where there are no fixed heights anywhere.

I stumbled across this SO answer Align vertically using CSS 3 which was my inspiration.

vertically centered text

Firstly, using an all % design, you need a zero height wrapper element to act as a positioning placeholder within the parent element;

<body>
<div class="container">
    <div class="divWrapper">
        <div class="tx">This text will center align no matter how many lines there are</div>
    </div>
</div>
</body>

My Container in this case is a simple box tile;

.container
{
    margin:2%;
    background-color:#888888;
    width:30%;
    padding-bottom:30%; /* relative size and position on page  */
    float: left;
    position:relative;  /* coord system stop */
    top: 0px; /* IE? */
}

So nothing special about that except that it has no height which makes this general problem of centering elements tricky. It needs to be absolutely positioned so that we can uses positioning coordinates in the child elements (I think this may require a 'top' in IE).

Next, the wrapper which is absolutely positioned to exactly overlay the parent element and fill it out completely.

.divWrapper
{
     position:absolute;
     top:0px;
     padding-top:50%; /* center the top of child elements vetically */
     padding-bottom:50%;
     height:0px;
}

The padding means that any child elements will start in exactly the middle of the parent element but this wrapper itself has no height and takes up no space on the page.

Nothing new yet.

Finally, the child element we want to center. The trick here to this was to have the child element slide up vertically based on it's own height. You can't use 50%, because that's 50% of the parent container not ourself. The deceptively simple answer is to use a transform. I can't believe I didn't spot this before;

.tx
{               
    position: relative;
    background-color: transparent;
    text-align: center; /* horizontal centering */

    -webkit-transform: translateY(-50%); /* child now centers itself relative to the  midline based on own contents */
    -moz-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    -ms-filter: 'progid:DXImageTransform.Microsoft.Matrix(M11=0.5, M12=0, M21=0, M22=0.5,  SizingMethod="auto expand")'; /*IE8 */
    filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.5, M12=0, M21=0, M22=0.5,  SizingMethod='auto expand'); /*IE6, IE7*/
    transform: translateY(-50%);    
}

Here's the Fiddle

However, I haven't tested this on IE6+ so if somebody would like to verify my Matrix transform I'd appreciate it.

Update

It turns out that the wrapper isn't even needed. This is all you need to properly vertically center;

.tx
{       
    width:100%;        // +1 to @RonM
    position: absolute;
    text-align: center;
    padding-top:100%;
    -webkit-transform: translateY(-50%); /* child now centers itself relative to the midline based on own contents */
    -moz-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    -o-transform: translateY(-50%);
    -ms-filter: 'progid:DXImageTransform.Microsoft.Matrix(Dx=0,Dy=0)'; /*IE8 */
    filter: progid:DXImageTransform.Microsoft.Matrix(Dx=0,Dy=0); /*IE6, IE7*/
    transform: translateY(-50%);    
}

And the updated Fiddle

But still not working in IE6 yet - looking at those transforms, I don't think this can be done for that legacy without JavaScript.

Community
  • 1
  • 1
cirrus
  • 5,624
  • 8
  • 44
  • 62
  • 3
    If you have a single word it will not be centered. You need to add width: 100% and height: 100% to your "tx" class. – Ron M Jul 08 '13 at 21:36
  • Cool. thanks @RonM. I hadn't spotted that, although I think it only requires width:100% so I've edited my post to reflect that. +1 to you. – cirrus Jul 08 '13 at 21:47
2

The reality is, that the only tags in HTML that have native fluid vertical alignment are the table cells.

CSS does not have anything that would get you what you want. Not today.

If the requirements are: 1. Works with every browser 2. fluid height 3. vertical centering 4. no scripting 5. No TABLEs 6. You want the solution today, not in few years

You are left with 1 option: 1. Drop ONE of your requirements

Otherwise this "puzzle" is not completable. And this is the only complete acceptable answer to your request.

... if only I could get all the salaries for the wasted hours on this particular challenge :)

Jani Hyytiäinen
  • 5,293
  • 36
  • 45
2

Don't self-abuse; let IE7 go... :) (According to this, not very many people are using it.)

I gave this a shot with two approaches, one uses display: table and the other uses line-height. Unfortunately, I don't have access to a PC, so they're only tested in Chrome 25.0.1365.1 canary, FF 18, and Safari 6.0 on Mac 10.8.1, iOS 6.0.1 Safari, and iOS Simulator 5.0 and 5.1 Safari.

The display: table approach has issues on iOS Simulator 5.0 and 5.1, the text isn't quite centered, vertically.

According to quirksmode, the display:table method should be compatitible with IE8 and up. Theorectically, the line-height method might be compatible with IE 6/7.

To create the centered box within each square, I set .box6 to position: relative and changed the .bc style to:

.bc  {
    position:absolute;
    top: 30%;
    bottom: 30%;
    left: 30%;
    right: 30%;
    overflow: hidden;
}

Each approach creates a very tall container with a static height inside the .bc element. The exact value for the static height is arbitrary, it just needs to be taller than the content it will contain.

The display: table method changes the .bbl and .bbl .lbl styles to:

.bbl {
  display: table;
  height: 500px;
  padding-top: 50%;
  margin-top: -250px;
  background-color: blanchedalmond;
  width: 100%;
}

.bbl .lbl {
  display: table-cell;
  vertical-align: middle;
  text-align:center;
}

For the line-height method, the HTML is:

<div class="bc">
    <div id="line-h-outter">
        <span id="line-h-inner">a lot more text than in the other blob. The quick brown fox jumped over the lazy dog</span>
    </div>
</div>

CSS:

#line-h-outter {
  line-height: 500px;
  vertical-align: middle;
  margin-top: -250px;
  padding-top: 50%;
}

#line-h-inner {
  display: inline-block;
  line-height: normal;
  vertical-align: middle;
  text-align: center;
  width: 100%;
}

http://jsfiddle.net/X3ZDy/93/

tiffon
  • 5,040
  • 25
  • 34
  • 1
    I think the browser share depends on demographics. In Enterprise business for instance (not that such stats exist to my knowledge, but based on personal experience) I believe the concentration of IE, and legacy IE in particular to be higher. Having said that, this is a well thought out and researched answer, that works, with some constructive backup. Yours *might* be the first to embrace the relationship between line-height and vertical-align. Not that there aren't other good answers here too, but thanks! – cirrus Jan 09 '13 at 10:39
  • good down to IE8 version, not working in IE7, tested with IE10 debug toolbar, possibly things will be different with real browser – dmi3y Jan 09 '13 at 12:26
  • @cirrus Awesome, thanks! And, you're probably right, some demographics are probably very under-represented in the [statcounter browser metrics](http://gs.statcounter.com/#browser_version-ww-monthly-201211-201211-bar), especially [Asia](http://gs.statcounter.com/faq#sample-size), where IE is popular, too. – tiffon Jan 10 '13 at 11:37
0

The title should be Centering in the Unknown ;-)

I updated your example using tables: http://jsfiddle.net/uWtqY/ and the text is align inside the box you described using tables, but you don't want this.

Added a table with like:

<table width="100%" height="100%"><tr><td>One line</td></tr> </table></div>

inside <div class="lbl">

Just for cross-browser support.

EDIT

After doing some research indeed it is really hard to v-align an element inside percentages. Tried a lot of stuff but your code I am not sure if it fits the design of all of them. Well what I mean in other words is that you might first need to construct your vertical alignment and then try to play with percentages. From my experience in this field I learned that a good approach is start designing from the inside elements and then go out if complexity increases. So having percentages in everything might not be the best implementation (and it is not when coming to mobile devices).

EDIT 2

After consolidating with several of my work partners and really geeks on the area of HTML the answer was clear. Either you support < IE7 and you do that with a table or ghost elements or spans, either you use all of the tequniques that are described in several posts and you can have support for >=IE7 . Another option is to use specific structure for each browser.

The link that I think explains it as it is and has a nice title (basically is what you need):

-> Centering in the Unknown

Hope the best.

PS. Links for reference:

Let me know if it helps

Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
  • thanks for your answer, but your fix has a
    tag. I'm also unclear on why you think I'm "missing a css height". I saw the jakpsatweb solution before but I couldn't get it to work for me with my relative height and widths. I believe there are similar issues with the image trick. I'd be interested in seeing a working jsFiddle though if you can do it with cross-browser relative sizing as per my example.
    – cirrus Jan 02 '13 at 16:26
  • Sorry I though you needed that height, my mistake.Yes my fix was with a table and I tested with some (not all) browsers and seems to be ok. I suppose you don't need an extra table for that. Nevertheless the issue with centering divs has always been a pain in the ass. Good luck with whatever implementation you choose. – Jimmy Kane Jan 02 '13 at 16:29
  • Correct me if i'm wrong but isnt 40% x 4 = 160%? Doesnt that equate to MORE than the width of the screen? – Zeddy Jan 02 '13 at 17:04
  • @cirrus Why did you strike it though? It works but with a table element. If that is something you don't wont then don't edit the answers with strike through and make your question better. – Jimmy Kane Jan 02 '13 at 17:10
  • @ZafKhan it's not mine this one you are talking about. – Jimmy Kane Jan 02 '13 at 17:11
  • @JimmyKane That question was for the person who asked the question int he first place, Sorry I'm still gettingusedto the Stack Overflow website editor/input fields – Zeddy Jan 02 '13 at 17:27
  • @JimmyKane - I took out the "fix" as per my comment. I don't want people to think that the update was the example to work with. Regarding tables, the title clearly states this is a "DIV" problem. Obviously, it's very easy with tables. I also dismissed table-layout in the question. – cirrus Jan 02 '13 at 17:33
  • @ZafKhan - sorry, I've clarified the question to avoid confusion about the 40%. – cirrus Jan 02 '13 at 17:36
  • @cirrus yes now it's more clear. Just trying to help you know. – Jimmy Kane Jan 02 '13 at 17:50
  • @JimmyKane I know - I appreciate that. – cirrus Jan 02 '13 at 18:01
  • @JimmyKane thanks. Did you manage to prove that method with a fiddle? I *am* using a responsive, fluid grid layout system, hence all their percentages. Square DIVs here, are the acid test. It works great under mobile. It might be that table is the only way to do this for legacy browsers, but I didn't say it was an easy problem :) I was reading everywhere that you couldn't do a non-js fluid grid system where the boxes stayed square at all screen widths either until I'd cracked it. – cirrus Jan 03 '13 at 10:56
  • No did not manage. It's not compatible with all browsers. I searched, and searched, had nightmares, because it's also a problem that seems to be easy but it isn't. The thing is that this thing will never work correctly without table netiquettes or other, that I don't like because they add complexity. I follow one rule: "there should be one obvious way to do it." – Jimmy Kane Jan 03 '13 at 10:59
  • 1
    Exactly. It looks easy, but it isn't so obvious. The thing is, TABLE is almost always thrown up as the (first) solution to a DIV problem. The same was true of my fluid grid, but in the end, it usually turns out that there's a little a table cell can do that you can't configure a DIV to do...if you can find it. – cirrus Jan 03 '13 at 11:03
0

I updated the css to utilize a "spacer" class. It is placed before your "bc" div and inside the colored boxes. This gave me the effect I think you requested.

html:
<div class="rgCol boxCol box6" style="background-color:lightgray">
        <div class="spacer"></div>
        <div class="bc">


css
.spacer {width:100%;padding-bottom:30%;display:block; }

I bottom padded the spacer by 30% and then moved the absolute left position of your "bbl" div to 30% (from 2%). The blanchdelemond boxes retain their shape and size.

http://jsfiddle.net/X3ZDy/37/

  • thanks. but that looks like it centers the bbl within the boxcol. I need the lbl centered within the bbl. – cirrus Jan 04 '13 at 21:21
  • Ok, I went back to your original fiddle and made a couple of changes. The important ones were: removing padding-top from the bbl and lbl style elements. I added a new element definition for lbl to text-align:center and padding-top:25%. The boxes remain where they are but the text is centered top to bottom (roughly -adjust the percentage as needed of course) and left to right. http://jsfiddle.net/X3ZDy/40/ – Kevin Whatever Jan 05 '13 at 15:26
  • yes, it's a good approach. I tried that myself as you'd think it would maintain relatives, but unfortunately, it's little more than just spacing. Now, say for arguments sake that we could control the 25% width and 25% padding top and statically code them to sync (which perhaps we could potentially) it still breaks down with multi-line because you can't compensate for an unknown number of lines. In fact with enough lines the top of lbl needs to go negative in order to vertically center, but even if you did, you don't know how much to pad because of the multiline. It's very tricky. – cirrus Jan 07 '13 at 10:13
0

Today I have stumbled upon similar problem - to both vertically and horizontally center child element of a square divs which have precentually set width (they are made sqare using the padding technique). I had to use it for images while maintaining aspect ratio, but changing the child into any target element would be simple.

For this situation no line-height, margin/padding or display:table-cell workaround is suitable. But there is a solution using margin: auto.

HTML:

<div class="squareContainer>
    <div class="contentWrapper">
        <img class="imagePreview" alt="Image preview" src="//URL.jpg">
    </div>
</div>

CSS:

div.squareContainer {
    position: relative;
    box-sizing: border-box;
    height: 0;
    padding-bottom: 25%;
    width: 25%;
}
div.contentWrapper {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
}
img.imagePreview {
    display: block;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
    margin: auto;  /* This is the important line */
    max-height: 90%;
    max-width: 90%;
}

Helpful resources:

http://jsfiddle.net/mBBJM/1/

http://codepen.io/HugoGiraudel/pen/ucKEC

Hope that helps!

helvete
  • 2,455
  • 13
  • 33
  • 37
0

You can solve this trivially, without all the weirdness (perhaps someday they'll fix the CSS box model, but till then):

<table>
<tr>
<td width="50" height="50" align="center" valign="middle">text</td>
</tr>
</table>

That's all there is to it. Choose your width and height, drop it in a div and call it good.

The idea of never using tables is a very poor guideline, to the point of being self-abusive.

fyngyrz
  • 2,458
  • 2
  • 36
  • 43
-2

Do you mean like this?

IMAGE

<div class="mycontainer">
  <div class="rgRow">
    <div class="rgCol" style="background-color:pink">
      <div class="boxCol">BOX1</div>
    </div>
    <div class="rgCol" style="background-color:lightgray">
      <div class="boxCol">
        <div class="boxLabel">a lot more text than in the other blob. The quick brown fox jumped over the lazy dog</div>
      </div>
    </div>
    <div class="rgCol" style="background-color:maroon">
      <div class="boxCol">
        <div class="boxLabel">One liner</div>
      </div>
    </div>
    <div class="rgCol" style="background-color:yellow">
      <div class="boxCol">BOX4</div>
    </div>
  </div>
</div>


.mycontainer
{
    background-color: #000000;
    display: inline-table;
}

.rgRow
{
    width: 100%;
    display: table-row;
}

.rgCol
{
    width: 25%;
    height: 100%;
    text-align: center;
    vertical-align: middle;
    display: table-cell;
}

.boxCol
{
    display: inline-block;
    width: 100%;
    text-align: center;
    vertical-align: middle;
}

.boxLabel
{
    text-align: center;
    vertical-align: middle;
    background-color: blanchedalmond;
    overflow: hidden;
    margin: 2%;
    width: 96%;
    height: 96%;
}
Zeddy
  • 2,079
  • 1
  • 15
  • 23
  • Not quite. Here's your fiddle: http://jsfiddle.net/eGywB/ Notice that the boxes are no longer square? That's the magic that (width == padding-bottom) provides. – cirrus Jan 03 '13 at 10:46
  • But everything elseis right isnt it? it doesnt use tables and it uses CSS and HTML only? And for what reason someone has mareked medown when my solution has been the closet is very dissappointing indeed - SPECIALLY when no one ELSE is marked down – Zeddy Jan 03 '13 at 15:18
  • Hi @Zaf. I don't know who first downvoted your solution, but it's not the closest because it ignores vital, key constraints in the question. #1 you pasted code rather than create a jsFiddle, #2 it uses table-cell, #3 it must maintain square, proportional DIVs. It's easy to come up with a version that works without those constraints, but I wouldn't have posted here if it was that easy :) – cirrus Jan 03 '13 at 17:55