How fill space between margin-collapsed siblings? - html

Question 1: Is there any way of automatically having an element fill the space between two margin-collapsed siblings, without adding to the space between the two?
Question 2: If not, is there any way of knowing the actual (after margin collapsing) space between two elements when margins are collapsed?
Background: I have a page with a vertical list of items. These items have different vertical spacing (i.e. margin)s depending on their content -- i.e. an image item has top and bottom margin of 100px, a text item has 50px vertical margin. In order to have good vertical rhythm, margin collapsing is used intentionally.
Now, I need "filler" elements that each take up all the vertical space between each item. For example, between an image item and text item, the "filler" would need to be 100px tall to fill the margin-collapsed space between the two items. Here's an example, with the pink filler filling all the space:
Problem: The list of items is dynamic, so I don't know ahead of time how much space will be in between the items. Because the margins collapse, I can't know ahead of time how tall to make the filler element. I am hoping to avoid having to spell out filler height based on every permutation of item combinations, each with its own value of the spacing between the two item types.
I can't have the filler element affect the space between the two elements (so it's position: absolute). Since it's position: absolute, I don't see a way of anchoring it to both the item above it and below (only to one of them).
Here is an example of manually setting the height for the filler to fill the space: https://jsfiddle.net/41pgv63s/
Is there any other way of being able to fill the space between two elements that are margin-collapsed, without increasing the space between them? I'd like to not have to manually look at the previous/next item and calculate the filler's height based on that. I'm hoping against hope that there's a CSS-only way to fill the space between the two items, so I don't need to know the context between every neighboring item in the list before setting the height of the filler item.

There are many solutions to this, but the quickest came to my mind was this:
.in-between-dweller {
background-color: rgba(255,128,255, 0.3);
position: absolute;
bottom: -100%;
left: 0;
height: 100%;
width: 100%;
}
.container {
position: relative;
}
.box {
height: 100px;
border: 1px solid #AAA;
background-color: #FAFFBB;
}
.one {
margin-bottom: 50px;
}
.two {
margin-top: 100px;
}
.in-between-dweller {
background-color: rgba(255,128,255, 0.3);
position: absolute;
bottom: -100%;
left: 0;
height: 100%;
width: 100%;
}
<div class="container">
<div class="box one">Box One (margin-bottom: 50px)</div>
<div class="in-between-dweller">I'm supposed to cover all the space between the two boxes...</div>
</div>
<div class="container">
<div class="box two">Box Two (margin-top: 100px)</div>
</div>

Related

How negative margin width affect float? [duplicate]

A common trick for vertical positioning elements is to use the following CSS:
.item {
position:absolute;
top:50%;
margin-top:-8px; /* half of height */
height: 16px;
}
When seen in the metric view as in Chrome this is what you see:
However, there is no visual margin depicted when you hover over the element i.e. the margin is 'outside' the border and can be visualized. But negative margins don't show up. How do they look and what is it that makes it different?
Why is margin-top:-8px not the same as margin-bottom:8px?
So just how do negative margins work and what's the intuition behind them. How do they 'bump up' (in case of margin-top < 0) an item?
Negative margins are valid in css and understanding their (compliant) behaviour is mainly based on the box model and margin collapsing. While certain scenarios are more complex, a lot of common mistakes can be avoided after studying the spec.
For instance, rendering of your sample code is guided by the css spec as described in calculating heights and margins for absolutely positioned non-replaced elements.
If I were to make a graphical representation, I'd probably go with something like this (not to scale):
The margin box lost 8px on the top, however this does not affect the content & padding boxes. Because your element is absolutely positioned, moving the element 8px up does not cause any further disturbance to the layout; with static in-flow content that's not always the case.
Bonus:
Still need convincing that reading specs is the way to go (as opposed to articles like this)? I see you're trying to vertically center the element, so why do you have to set margin-top:-8px; and not margin-top:-50%;?
Well, vertical centering in CSS is harder than it should be. When setting even top or bottom margins in %, the value is calculated as a percentage always relative to the width of the containing block. This is rather a common pitfall and the quirk is rarely described outside of w3 docos
I'll try to explain it visually:
/**
* explaining margins
*/
body {
padding: 3em 15%
}
.parent {
width: 50%;
width: 400px;
height: 400px;
position: relative;
background: lemonchiffon;
}
.parent:before,
.parent:after {
position: absolute;
content: "";
}
.parent:before {
top: 0;
bottom: 0;
left: 50%;
border-left: dashed 1px #ccc;
}
.parent:after {
left: 0;
right: 0;
top: 50%;
border-top: dashed 1px #ccc;
}
.child {
width: 200px;
height: 200px;
background: rgba(200, 198, 133, .5);
}
ul {
padding: 5% 20px;
}
.set1 .child {
margin: 0;
position: relative;
}
.set2 .child {
margin-left: 75px;
position: relative;
}
.set3 .child {
margin-left: -75px;
position: relative;
}
/* position absolute */
.set4 .child {
top: 50%;
left: 50%;
margin: 0;
position: absolute;
}
.set5 .child {
top: 50%;
left: 50%;
margin-left: 75px;
position: absolute;
}
.set6 .child {
top: 50%; /* level from which margin-top starts
- downwards, in the case of a positive margin
- upwards, in the case of a negative margin
*/
left: 50%; /* level from which margin-left starts
- towards right, in the case of a positive margin
- towards left, in the case of a negative margin
*/
margin: -75px;
position: absolute;
}
<!-- content to be placed inside <body>…</body> -->
<h2><code>position: relative;</code></h2>
<h3>Set 1</h3>
<div class="parent set 1">
<div class="child">
<pre>
.set1 .child {
margin: 0;
position: relative;
}
</pre>
</div>
</div>
<h3>Set 2</h3>
<div class="parent set2">
<div class="child">
<pre>
.set2 .child {
margin-left: 75px;
position: relative;
}
</pre>
</div>
</div>
<h3>Set 3</h3>
<div class="parent set3">
<div class="child">
<pre>
.set3 .child {
margin-left: -75px;
position: relative;
}
</pre>
</div>
</div>
<h2><code>position: absolute;</code></h2>
<h3>Set 4</h3>
<div class="parent set4">
<div class="child">
<pre>
.set4 .child {
top: 50%;
left: 50%;
margin: 0;
position: absolute;
}
</pre>
</div>
</div>
<h3>Set 5</h3>
<div class="parent set5">
<div class="child">
<pre>
.set5 .child {
top: 50%;
left: 50%;
margin-left: 75px;
position: absolute;
}
</pre>
</div>
</div>
<h3>Set 6</h3>
<div class="parent set6">
<div class="child">
<pre>
.set6 .child {
top: 50%;
left: 50%;
margin: -75px;
position: absolute;
}
</pre>
</div>
</div>
Margin is the spacing outside your element, just as padding is the spacing inside your element.
Setting the bottom margin indicates what distance you want below the current block. Setting a negative top margin indicates that you want negative spacing above your block. Negative spacing may in itself be a confusing concept, but just the way positive top margin pushes content down, a negative top margin pulls content up.
good points already made here, but while there is lots of information about how rendering of margins is accomplished by the browser, the why isn't quite answered yet:
"Why is margin-top:-8px not the same as margin-bottom:8px?"
what we also could ask is:
Why doesn't a positive bottom margin 'bump up' preceding elements, whereas a positive top-margin 'bumps down' following elements?
so what we see is that there is a difference in the rendering of margins depending on the side they are applied to - top (and left) margins are different from bottom (and right) ones.
things are becoming clearer when having a (simplified) look at how styles are applied by the browser: elements are rendered top-down in the viewport, starting in the top left corner (let's stick with the vertical rendering for now, keeping in mind that the horizontal one is treated the same).
consider the following html:
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
analogous to their position in code, these three boxes appear stacked 'top-down' in the browser (keeping things simple, we won't consider here the order property of the css3 'flex-box' module). so, whenever styles are applied to box 3, preceding element's positions (for box 1 and 2) have already been determined, and shouldn't be altered any more for the sake of rendering speed.
now, imagine a top margin of -10px for box 3. instead of shifting up all preceding elements to gather some space, the browser will just push box 3 up, so it's rendered on top of (or underneath, depending on the z-index) any preceding elements. even if performance wasn't an issue, moving all elements up could mean shifting them out of the viewport, thus the current scrolling position would have to be altered to have everything visible again.
same applies to a bottom margin for box 3, both negative and positive: instead of influencing already evaluated elements, only a new 'starting point' for upcoming elements is determined. thus setting a positive bottom margin will push the following elements down; a negative one will push them up.
A margin-top of -8px means it will be 8px higher than if it had 0 margin.
A margin-bottom of 8px means that the thing below it will be 8px further down that if it had 0 margin.
Because you have used absolute positioning, and specified a top percentage, only margin-top will affect the location of your .item object. If instead you positioned it using bottom: 50%, then you'd need margin-bottom -8px to centre it, and margin-top would have no effect.
Margin affects the boundaries of an element in terms of positioning it, either absolutely as in your case, or relative to neighbouring elements. Imagine that margin is the foundations of your element on which it sits. They are typically the same size as it, but can be made larger or smaller on any or all of the four edges.
Your CSS tells the browser to position the top of your element the margin at a point 50% of the way down the page. However, as all elements are not a single pixel, the browser needs to know which part of it to line up 50% of the way down the page. For lining up the top of the element, it uses the top margin. By default this is in line with the top of the element, but you can alter it with CSS.
In your case, top 50% would result in the top of the element starting in the middle of the page. By applying a negative top margin, the browser uses the point 8px into the element from the top (ie the line across the middle of it) as the place to position at 50%.
If you apply a positive margin to the bottom, this extends the line the browser uses to position the bottom out away from the element itself, giving a gap between it and any adjacent element below, or affecting where it is placed absolutely if positioning based on the bottom.
I wonder if this question has been answered well: how css margins work and why is it that margin-top:-5; is not the same as margin-bottom:5;?
Margin is distance from the surroundings of the element. margin-top says "... distance from surroundings as we measure from top 'side' of the element's 'box' and margin-bottom being the distance from the bottom 'side' of the 'box'". Then margin-top:5; is concerned with the top 'side' perimeter,-5 in that case; anything approaching from top 'side' can overlap top 'side' of element by 5, and margin-bottom:5; means distance between element bottom 'side' and surrounding is 5.
Basically that but affected by float'ed elements and the like: http://www.w3.org/TR/CSS2/box.html#margin-properties.
http://coding.smashingmagazine.com/2009/07/27/the-definitive-guide-to-using-negative-margins/
I stand to be corrected.
Just to phrase things differently from the great answers above, as that has helped me get an intuitive understanding of negative margins:
A negative margin on an element allows it to eat up the space of its parent container.
Adding a (positive) margin on the bottom doesn't allow the element to do that - it only pushes back whatever element is below.

Why doesn't "margin: auto" center an element vertically?

As you can see in the demo below, margin: auto; centers the blue div horizontally, but not vertically. Why not?
.box {
border: 1px solid red;
width: 100px;
height: 100px;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
}
<div class="box">
<div class="center"></div>
</div>
My question is not asking for workarounds.
As mentioned, this behavior is specified in section 10.6.2 of CSS2.1, and has remained unchanged from CSS2.
Block boxes are stacked vertically from top to bottom in normal flow. Furthermore, vertical margins may collapse, and only do so under certain circumstances (in your demo, the border on the parent element will prevent any margins on the child element from collapsing with its own). If you only have one such block box, and the height of the containing block is auto, then its top and bottom margins will be zero anyway. But if you have more than one block box in the same flow, or even out-of-flow boxes affecting the layout of in-flow boxes (in the case of clearance for example), how would you expect auto margins to resolve for those in-flow boxes?
This is why auto left and right margins are likewise zeroed out for inline elements (including atomic inlines) and floats (though horizontal margins never collapse). Inline-level boxes are laid along line boxes, and floats too obey unique layout rules.
Absolutely positioned boxes are a different story: since they are never aware of any other boxes in the same positioning context as themselves, auto top and bottom margins can be calculated for them with respect to their containing blocks without having to worry about any other boxes ever interfering.
Flexbox is also a different story: what sets flex layout apart from block layout is that flex items are by definition always aware of other flex items in the same flex formatting context, including the fact that there are none. In particular, neither can floats intrude into the flex container, nor can you float flex items to subvert this (although you can still remove a child element from flex layout completely with absolute positioning). Margins behave very differently with flex items due in part to this. See sections 4.2, 9.5 and 9.6.
Why...because the W3C spec says so.
If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
As to the actual "why"...the query should really be addressed there.
It doesn't center the element vertically because it is a block-level element in the normal flow. Thus, the following rule applies:
If margin-top, or margin-bottom are auto, their used value is 0.
It's also worth pointing out that the rule above also applies to the following elements as well: (see points 10.6.2 and 10.6.3 for more information and conditions).
Inline replaced elements
Block-level replaced elements in normal flow
inline-block replaced elements in normal flow
Floating replaced elements
Block-level non-replaced elements in normal flow when overflow computes to visible
With that being said, absolutely positioned, non-replaced elements that don't have top, height, and bottom values of auto are an exception to this rule. The following applies from point 10.6.4:
If none of the three top, height, and bottom are auto and if both margin-top and margin-bottom are auto, solve the equation under the extra constraint that the two margins get equal values.
See the example below demonstrating how an absolutely positioned element is vertically centered using margin: auto. It works because none of the three properties top, height, and bottom have a value of auto:
.box {
border: 1px solid red;
width: 100px;
height: 100px;
position: relative;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
position: absolute;
top: 0; right: 0;
bottom: 0; left: 0;
}
<div class="box">
<div class="center"></div>
</div>
In addition, it's probably worth pointing out the following rule as well:
If one of margin-top or margin-bottom is auto, solve the equation for that value. If the values are over-constrained, ignore the value for bottom and solve for that value.
This means that if the absolutely positioned element has a margin-top value of auto and a margin-bottom value of 0 (i.e., margin: auto auto 0), the element would be absolutely positioned at the bottom relative to the parent like in the example below:
.box {
border: 1px solid red;
width: 100px;
height: 100px;
position: relative;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto auto 0;
position: absolute;
top: 0; right: 0;
bottom: 0; left: 0;
}
<div class="box">
<div class="center"></div>
</div>
Why doesn't margin:auto work vertically?
Actually, it does – just not for every display value.
If display is flex, margin: auto centers both vertically and horizontally.
The same applies to display: inline-flex, display: grid and display: inline-grid.
.box {
border: 1px solid red;
width: 100px;
height: 100px;
display: flex; /* new */
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
}
<div class="box">
<div class="center"></div>
</div>
It's because of the actual possibility of knowing the true height of the element in which you want to center vertically in. To understand that, first think about how auto horizontal centering works. You have a div which you've given it a width (fixed or percentage). The width can be calculated to certain degree. If it's fixed width, great. If it's flexible or responsive (percentage) at least you have a range that the width will cover before it hits the next breakpoint. You take that width, minus whatever it's inside and split the remainder on both sides.
Now, with that information, how could the browser calculate the infinite amount of variations in which your div will grow vertically? Keep in mind the size of the element, wrapping of text, paddings, and responsiveness will also alter the width and force the text to wrap further, and on, and on it goes.
Is it an impossible task? Not really, has CSS spent time and effort covering this? Not worth their time, I guess.
And that is basically the answer I tell my students.
But....fret not! Bootstrap v4 alpha has figured out vertical centering!
EDIT
Sorry to edit this late but I thought you may want to consider this solutions to center vertically and it is pretty simple by making use of the calc function
<div class="foo"></div>
.foo {
background-color: red;
height: 6em;
left: calc(50% - 3em);
position: absolute;
top: calc(50% - 3em);
width: 6em;
}
See it HERE

100% height of window with inner div scrolling

I've been banging my head against the wall really hard for the past couple of hours to figure out a way to achieve the layout I'd like for a webapp. And my head hurts.
Basically what I need is to have a full window layout (full width, full height, no scrolling - ever). 100% of width and height should be covered using two different horizontal boxes (you can see them as rows).
The height of the first box/row can be variable (see it as a header for the page)
The one below should occupy what's left of the space, without ever going further than 100% of the window, hence without ever showing a scrollbar.
Now what's a bit more tricky is that within the second box/row, I want content to be displayed with an inner vertical scrolling. Imagine the second box/row contains a list of items, in case of very few items, the bottom part of the box/row should stop right after the content. In case of many items, the box/row should expand right until it hits 100% of the window height (which is basically 100% of the windows - the height occupied by the first box/row). The rest of the content should be visible through scrolling within the second box/row.
Am I making any sense?
Regarding the code, I'm not going to copy/paste the desastrous thing I've pulled together because I'd rather start from a blank page.
This is what I tried:
<html>
<body>
<div id="wrapper">
<div class="box">Header</div>
<div class="box">Content <ul><li>...</li>(x1000)</ul></div>
</div>
</body>
</html>
The reason why I use a "box" class is because both boxes/rows should show the same appearence in terms of backgrounds, margins, shadows, etc.
html, body {
height: 100%;
}
#wrapper {
position: absolute;
height: 100%;
width: 100%;
left: 15px;
right: 15px;
top: 15px;
bottom: 15px;
}
For the rest, I've just tried (and failed so far) to manipulate the .box elements by adding hazardously overflow: hidden; overflow-y: scroll; height: 100%; max-height: 100%; min-height: 100%; etc.
Thanks in advance for your help!
The problem is because CSS has long been crappy about auto-adjusting height to available space.
The solution is to use a wrapper that's set to position: absolute and tied to the top, left, right, and bottom edges of the viewport. With this, the browser will auto adjust the height of the element, and if you have a content div inside with height: 100% it'll always fill that space.
Setting overflow-y: scroll on the wrapper will allow the content to scroll if it becomes too long:
http://codepen.io/helion3/pen/jwbcx
Site headers are usually not variable in height. If you're defining the site header using percentages, and if you don't need to support IE<8 then you can use percentages safely with box-sizing: border-box to achieve the same.
I believe this should do the trick.
If you adjust the height of .header make it is equal to the top: position of .content
CSS:
html, body {
margin: 0;
}
.header {
height: 150px;
background: #0080ff; // (Unnecessary, this is set to help you see the header div)
}
.content {
position: absolute;
top: 150px;
bottom: 0;
left: 0;
right: 0;
overflow: auto;
background: #ff8000; // (Unnecessary, this is set to help you see the content div)
}
HTML:
<body>
<div id="wrapper">
<div class="box header">Header</div>
<div class="box content">Content</div>
</div>
</body>
Maybe you want something like this? I replaced class="box" with ids, but it should work.
Consider following things:
No need to have the "absolute" positioned div (#wrapper in your example)
Create 2 box div same like you have created in your example (.box)
Second box should have "overflow:auto" style property
Calculate the height of header and full display area's height with javascript
Calculate the remaining height and assign this value as height, min-height and max-height for the second box. That's it.
You can check the solution here:
http://webnflash.com/temp/occupyAvailableHeight.htm

How can I lay out two <div>s on one line, and have one centre-aligned (relative to the entire line) and the other right-aligned?

I want to display 2 divs in a single line. I have a parent div and two child divs.I want to keep the width of first child div and parent div equal. So the header(label of first child div) displays always middle position of parent div and I want to display the second child div at the right side in the same line of parent div.(Condition is always label of first child div should display middle of parent div). Here is the jsfiddle.
If I were styling this header section for a website, and I wanted some flexibility in styling the various elements, here is out I would start.
For my HTML:
<div class="head">
<div class="innerfirst">
<h1>ABCDEF GHIJ</h1>
</div>
<div class="innersecond">
<label>RIGHT1</label>
<label>RIGHT2</label>
</div>
</div>
I would put the page title in a <h1> tag so that I can adjust font-size, padding, background color and so on. In fact, you could add a tag line below the title line and various background images. Having .innerfirst and h1 gives you quite a bit of flexibility.
The <label> tags don't make sense semantically in this context, but perhaps you will have have input fields later like a search box.
For the CSS:
.head {
background-color:#2191C0;
width: 100%;
height: 85px;
position: relative;
}
The above is fine, set position: relative so that you can use absolute positioning for one of the child elements. The fixed height is a good idea, makes it easier to adjust elements vertically.
.innerfirst {
}
.innerfirst h1 {
text-align: center;
color: #FCFCFC;
padding-top: 10px; /* You could also use a margin... */
}
By default, .innerfirst will have 100% width since it is an in-flow block element, same with the h1 element. You can center the text within h1, and adjust color, padding and margin as needed.
.innersecond {
border: 2px solid lightgray;
color: white;
position: absolute;
width: 25%; /* Set this or by default it will shrink-to-fit content */
height: 61px; /* Set this or by default it will shrink-to-fit content */
top: 5px;
right: 5px;
padding: 5px;
}
What you could do is create a box of text and absolutely position it to the right. It is a good idea
to set a height and width otherwise, as a result of the absolute positioning, the div will shrink to fit the content, which is sometimes useful. The top and right offsets will position the .innersecond to the top-right of the parent container because you set position: relative in .head.
.innersecond label {
display: block; /* optional if you want block behavior */
border: 1px dotted white;
}
Finally, if you want the label tags to behave like blocks, use display: block and style according to you design requirements.
For reference, demo fiddle: http://jsfiddle.net/audetwebdesign/qpb9P/
Here's an updated jsfiddle. Read up on the display property!

css dynamic layout of two containers

I have an interesting layout design problem that I was wondering if I could get some help with.
There are two containers next to each other, for the most part the one to the right is statically sized. The one to the left should grow based on dynamically loaded content. which could have quite a bit, or just a single data set. Now, the right container should always be next to the left one as it grows.
Here comes the tricky part. The left container cannot wrap and should take up to the remaining width on the screen, at which point scroll bars appear. At this point, the right container is next to the edge of the screen.
I can get it to work where the left container will ALWAYS take up the remaining screen real estate, but this pushes the right container all the way right side even if there is only a little bit of data on the left.
I can also get the data to grow and have the right side stay next to the left, but then the overflow scrollbars never appear. I am at my wits end on how to solve all of these requirements.
I can also just set widths for everything but that doesn't really solve the requirements of growing up to the screen resolution, and still doesn't solve for only tiny amounts of data
edit: Attached an image of my current layout. This looks good because it is "full", taking 100% of the browser width. What i need is to have the right portion move to the left if the data columns become so small it no longer scrolls.
I put together a jsfiddle of what I think you're trying to accomplish.
The right side column is fixed in width, and position.
The left column will expand in width, and scroll if the width becomes too small to display the inside content.
Here is the example:
http://jsfiddle.net/MwdED/
Here is the HTML:
<div class="container">
<div class="col-left">
<div class="col-content">
<div class="wide-content">
THis is some wide content, sitting inside of the left column.
</div>
</div>
</div>
<div class="col-right">
<div class="col-content">
This is the right column. It's fixed in width.
</div>
</div>
</div>
And the CSS
.container {
position: relative;
min-width: 500px; /*make sure the columns don't collapse on top of each other*/
}
.col-left {
padding-right: 200px; /*make room for right column*/
}
.col-left .col-content {
min-width: 300px; /* the minimum width, before overflow scrolling occurs */
overflow: auto;
}
.col-right {
position: absolute;
right: 0;
top: 0;
width: 200px;
}
.wide-content {
background: red;
width: 1000px;
}
Taking what Axel provided, I thought for sure something like this would work. The intent was to make both containers fluid so that no matter which one grew or shrank, the other would take up the difference, but for some reason the containers bleed into each other.
.col-left .col-content {
min-width: 20%; /* the minimum width, before overflow scrolling occurs */
max-width: 80%;
overflow: scroll;
}
.col-right {
position: absolute;
right: 0;
top: 0;
min-width: 20%;
max-width: 80%;
}