Can I vertically align inline blocks based on their children? - html

To illustrate this:
div {
border: 1px solid #000;
}
.ib {
display: inline-block;
}
.vm {
vertical-align: middle;
}
<div class="container">
<div class="col ib vm">
<div>aaa<br>a</div>
<div>b<br><br>b</div>
</div>
<div class="col ib vm">
<div>a<br><br><br><br>a</div>
<div>b<br><br>bbbbbbbbbb</div>
</div>
</div>
Here we have two inline blocks (i.e. .col), and they're vertically aligned at the middle. The question is:
Can I make the .cols vertically aligned by the middle of the inner children? (For example, the children containing "b".)
Or do I have to use JavaScript to calculate the position and apply transform?
As each .col may contain multiple children and I have many such .cols, implementation without JavaScript would be great.
Any solutions with grid, or flex, layout are OK.
The desired result is like the following image:
The .cols should be vertically moved such that the middle of their second children (regardless of the middle of the two .cols) are aligned.

I don't think this is possible with only CSS. If you need exactly this behavior the best thing you can do is probably to calculate needed margin-top for appropriate column using JavaScript. With correct margin second rows will appear as vertically centered.
If overlapping behavior can be sacrificed, you can place content in rows instead of columns and vertically center contents of second row.

Related

When a flexbox item has wrapping text the item fills the container and has extra whitespace at the end

Using a flexbox layout, I have a container with three divs in it. What I'm going for is the first div hugging the left edge of the container while the remaining two divs snuggle up on the right edge of the container. I'm admittedly very new to using the flex layout, but it seems straightforward enough.
Simple html:
<div class="container">
<div class="one">Left</div>
<div class="two">Middle</div>
<div class="three">Right</div>
</div>
And associated css:
.container{
display: flex;
align-items: center;
}
.one{
margin-right:auto;
}
The above code usually displays exactly as intended, and for most cases is perfect. Where trouble rears up is when, for example, that third item has enough text to wrap. When this happens the third item fills with whitespace to the right of the text, leaving no space between items one and two. It is subtle with small words, but pretty obvious with two large words that break right in the middle. The following codepen explains it better than I can.
Codepen showing the issue: http://codepen.io/camwheel/pen/XjjyOx
Am I abusing/misusing flex syntax here, or is there a legitimate issue with wrapped text in a flex item? More importantly in the short term, is there anything I can do to get around this issue?
If your divs only contain text then you can do the following:
Skip flexing the last item
text-align: right on the middle element
.container {
display: flex;
background: salmon;
}
.one, .two {
flex: 1;
}
.two {
text-align: right;
}
<div class="container">
<div class="one">Left</div>
<div class="two">Middle</div>
<div class="three">Right</div>
</div>
As Michael_B pointed out in comments, this isn't a flex issue. It comes down to a limitation of CSS. Parent items don't know when child items shrink (which happens to the width when text wraps) and so can't collapse back down. For a more in depth assessment see his earlier post here.

Keeping flexbox container centered when child text wraps [duplicate]

This question already has answers here:
Make container shrink-to-fit child elements as they wrap
(4 answers)
Closed 5 years ago.
I am trying to center two div elements side by side in a row with flexbox (display:flex).
The first div element, on the left, just has an image.
The second div element, on the right, has left-aligned inline text of unspecified length.
When the line of text is short enough to fit on one line, both divs are aligned and justified to the center as I expect.
When the line of text is long enough to wrap onto two lines, the second div element does not wrap itself around the content as I expect. Instead, it leaves a large white space on the right side of the div.
I mocked up an example here: http://codepen.io/anon/pen/NGqYQX?editors=110. Vary your browser window's width to see what I mean.
How can I set the second div element to shrink itself to fit the text, so that both div elements appear centered?
.flexbox-container {
display: flex;
justify-content: center;
align-items: center;
}
.div1 {
margin-right: 30px;
}
.div2 {
font-size: 48px;
line-height: 48px;
text-align: left;
}
<div class="flexbox-container">
<div class="div1">
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</div>
<div class="div2">
This is an example of a line of text.
</div>
</div>
<br>
<div class="flexbox-container">
<div class="div1">
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</div>
<div class="div2">
This is an example of a much loooooooooooooonger line of text.
</div>
</div>
Here is a Photoshop mockup showing what I am trying to do:
In order to achieve your goal (as specified in your second image) we need to make a few adjustments to your HTML and CSS. Everything can be done with flexbox.
HTML
<div id="flex-container-main">
<div class="flex-container-child">
<figure>
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</figure>
<p>This is an example of a line of text.</p>
</div>
<div class="flex-container-child">
<figure>
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</figure>
<p>This is an example of a much loooooooooooooonger line of text.</p>
</div>
</div><!-- end #flex-container-main -->
CSS
#flex-container-main {
display: flex;
flex-direction: column;
align-items: center;
}
.flex-container-child {
display: flex;
align-items: center;
min-height: 127px;
width: 75%;
margin: 10px;
}
figure {
margin: 0 20px 0 0;
}
img {
width: 150px;
height: 127px;
vertical-align: bottom;
}
p {
font-size: 48px;
margin: 0;
}
Revised Codepen Demo
Here's what's happening...
Your question asks:
Keeping flexbox centered when text wraps to 2 or more lines
I am trying to center two div elements side by side in a row with flexbox (display:flex).
Let's quickly go over your two images.
Image 1
Image 2
In image 1 all flex items are actually centered. The blue highlight from Chrome Dev Tools emphasizes this point. Each item is perfectly centered on the screen.
Yes, it does get a bit clunky as you re-size the screen smaller – mostly because of the large font size – but the flex items remain centered nonetheless.
In image 2, the flex items are not evenly centered. What you've created in your mock-up is more like a column containing both flexboxes, and the column is centered. But individually only the first row is centered on the screen.
A couple of notes about your code:
With justify-content declared on the flex containers, you are centering the flex items. The flex container itself is not centered.
Since both flexboxes are direct children of the <body>, and <body> has no defined width, the flexboxes align themselves in relation to the viewport.
So to achieve the effect you want we can wrap all your existing mark-up in a new flex container (#flex-container-main). This converts the original flex containers into flex items, which can then be evenly centered as a group.
The new flex items (now classed as .flex-container-child) are given a width to create space and a minimum height based on the height of the image. Each flex item is also declared a flex parent (display: flex) which allows us to use flex properties on child elements. In particular, this is useful for vertically centering the text (as shown in your images).
(Note that my use of HTML semantic elements is not necessary for the code to work. If you prefer the original div tags just swap them back. The important adjustment is the new parent container.)
Lastly (and this may not be important to your layout but just in case), browsers normally give images a small gap of whitespace under their bottom border. This is used to accommodate descenders. With vertical-align: bottom, this space is removed. (For more details see my answer about descenders.)

Why does a divs top margin move multiple divs down?

Say I have 3 divs side by side:
<body>
<div id="ok1">Content for id "ok1" Goes Here</div>
<div id="ok2">Content for id "ok2" Goes Here</div>
<div id="ok3">Content for id "ok3" Goes Here</div>
</body>
Then I apply a margin to one of them:
#ok1 {
display: inline-block;
margin-top: 20px;
}
#ok2 {
display: inline-block;
}
#ok3 {
display: inline-block;
}
Why is it that all three get a top margin?
This is because the two divs are inline with the first one. When you did display: inline-block; it moved the other two divs into a block with the first one.
If you take that out, it goes back to normal.
fiddle
thanks to #MosheKatz for the idea on inline-block
An inline formatting context is established between the elements, therefore the following applies:
9.4.2 Inline formatting contexts (w3.org)
In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes. The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.
The default vertical-align value for inline elements is baseline.
You could change this value to something like top, thus changing the results (example)
div { vertical-align:top; }

Vertical middle for an element within a variable height container

I have the following situation:
A variable height div (#container) with an image on the inside (image that is placed within another div) that I need to float:right and align vertically in the middle. How to do this?
Thanks.
EDIT:
Maybe I didn't make it clear enough that i do not know beforehand how much content the container has, from a few lines to a wall of text, so any solution relying on its height won't work (and that's my problem :P)
This is a fiddle with an example of possible content to which align the image: http://jsfiddle.net/9DbmN/
You should take a look at Centering in the Unknown by Chris Coyier. Imo it´s a pretty solid solution to the holy grail of vertical centering.
I would not discourage using tables here))) If you use a two-cell table with vertical-align: middle set on its td elements - it will perfectly (and easily!!!) solve your problem.
If you want to have two containers, one of which (the one with the image) will be floated to the right and needed centering - I'd say you'll have to avoid using float property for this. Because a) as far as I understand you don't need you content on the left to be UNDER the image, right? b) floats are block-level elements and you can't change it even if you set display: table-cell, the browser will still render it as display: block - which leads me to the conclusion that you won't manage to center it by css (at least by the means I'm aware of).
If you don't need ie7 support a possible workaround might be this:
html:
<div id="container">
<div class="content">Content goes here, vertically aligned with the image</div>
<div class="i_used_to_be_floated_right">Image goes here</div>
</div>
css:
#container {
display: table;
width: 100%;
}
.content, .i_used_to_be_floated_right {
display: table-cell;
vertical-align: middle;
}
.content {
background: green;
width: 80%;
}
.i_used_to_be_floated_right{
background: red;
width: 20%;
}
The working example live can be seen here: http://jsfiddle.net/skip405/sDXMj/1/
But if you need ie7 - I would vote for the table-solution I stated at the very beginning.

Three part dynamic spacing wo/Table and Absolute Positioning

I'm trying to place 3 divs within a larger div such that the center one is 800px wide, and centered, and the other two fill the space remaining. I cannot use tables, nor can I use absolute positioning, as I have html below that must be outside the three divs but inside the larger div. I can get the center div:
.center-div {
width: 800px;
margin-left: auto;
margin-right: auto;
}
But how do I position the other two divs?
<div id="outer">
<div id="left-div"></div>
<div id="center-div"></div>
<div id="right-div"></div>
</div>
You could try messing around with display: table-row; for the container div and display: table-cell; for the inner divs. You might even need a second container with display: table;—the basic idea is emulating a table without using table, tr, and td.
All those table-values for the display property are specified in CSS 2.1, but I have never personally tested which browsers support them. I’ll bet my money though that IE6 won’t be able to cope with it. ;-)