Absolutely positioned div is too wide when padding: % - html

I have two divs, one position:static and one position:absolute. Despite having the same width and padding values, the absolutely positioned div gets about 17px wider. This only happens when I specify the horizontal padding as a percentage. How do I get these divs to be the same width without removing the % padding?
.foo {
border:2px solid blue;
background: pink;
width: 200px;
padding: 20px 20%;
}
.foo:nth-child(3) {
border-color: red;
position:absolute;
top:96px;
}
<div class="foo"></div>
<div class="foo"></div>
<div class="foo"></div>

I know how to fix this:
body {
width: 100%;
}
I'm just not 100% sure why this is happening. It probably has something to do with the absolute positioned element does not share the same containing element (body vs html) and that is what padding that uses percentages is based off of.

That's because percentages are resolved relatively to the containing block, which varies in the absolute case.
For the in-flow elements, the containing block is established by the body element, which by default has some margin, so it's narrower than the viewport.
For the absolutely positioned element, the containing block is established by the nearest positioned ancestor. Since there isn't any, it's the initial containing block, established by the viewport.
The solution is positioning body so that it establish the containing block for the absolutely positioned element:
body {
position: relative;
}
body {
position: relative; /* Establish the containing block for absolutely positioned descendants */
}
.foo {
border: 2px solid blue;
background: pink;
width: 200px;
padding: 20px 20%;
}
.foo:nth-child(3) {
border-color: red;
position: absolute;
top: 96px;
}
<div class="foo"></div>
<div class="foo"></div>
<div class="foo"></div>

The problem you're facing is in reality a pretty common one: default margin on the body.
By adding margin: 0; for the body, the issue is solved. The reason why padding: 20px 20%; behaves differently for the elements is because the percentage value is based on the width of the parent - for the statically positioned elements, this is reduced by the default margin. However, for the absolutely positioned element, this margin on the body is ignored (html rather than body is treated as its parent, which has no margin) and the 20% will be of a larger number.
This is also why you had to use top:96px; instead of top:88px (40px in padding + 8px in borders) for that absolutely-positioned element - the default margin pushed down the top two elements.
Here's your code, adjusted accordingly:
body {
margin: 0;
}
.foo {
border: 2px solid blue;
background: pink;
width: 200px;
padding: 20px 20%;
}
.foo:nth-child(3) {
border-color: red;
position: absolute;
top: 88px;
}
<div class="foo"></div>
<div class="foo"></div>
<div class="foo"></div>

I know this is an old one, but it shows up in the top spot in google for certain terms, so I thought I'd go ahead and give the correct answer.
Yes, this does have to do with inheriting parent, but not in the way described. Body inherits it's values from the html tag, meaning that the issue isn't with the body tag.
The clue is in the very specific 17 pixels. That is the default width of the scrollbar in chrome.
Looking at the code here, the width was specified in %. Setting the body to
width:100%
margin:0px
padding:0px
will set the body to be the same width as the tag
The tag in turn will be 100% of the AVAILABLE window, which means that if your content generates a scrollbar, that width is now -17 pixels wide.
The sollution is to set either your body or your html tag (yes, you can style that one too) to
width:100vw;
vw doesn't take the scrollbar into account, but instead only displays the windows width.
I hope that makes sense, and that I have managed to help you solve your problem.

Related

clearing after a float left creates a void between two divs

I have the following part of my html
<div class="header">
<div class="header-bar">
<div class="pull-left">
<div class="title">Ci models database</div>
</div>
</div>
<div class="clear-both"></div>
<ol class=breadcrumb>
<li class="active">All models</li>
</ol>
</div>
the css(breadcrumb and active classes are bootstrap)
.header-bar {
border: None;
background-color: #66CCFF;
min-height:30px;
}
.title {
padding: 5px 5px 10px 5px;
color: white;
font-size: large;
}
.clear-both{
clear:both;
}
But between header-bar and breadcrumb html added a white space(see bootply). How can I remove this white space, since no padding and margin can be found between to divs.
The problem is that the calculated height of the internal .title div is greater than the calculated height of the container .header-bar. Properties like height, min-height, border, padding can directly effect heights, whereas properties like display, box-sizing and position can all indirectly effect height.
The result is the internal .title div pushes down the next div in the flow by 10px.
CSS has no rules that say a div must contain it's children in height and stop them from effecting other divs, even when height is directly defined. We need to tell it exactly how it should behave when things are rendered.
There are several ways to fix this:
http://www.bootply.com/Qa1ME2M2uk - use overflow: hidden; on the parent. Overflow is a css property which is used how to control what happens when child elements are larger than their parents. It's worth noting that depending on other properties overflow won't necessarily render itself in a way that disrupts layout.
http://www.bootply.com/ssq3EAzeyk - set explicit heights to take strict control over the dimensions of the elements. This might be the best option for a header bar.
http://www.bootply.com/yeodYRLLJk - set a greater min-height on the parent, one which will definitely contain the child. This is useful if your padding is for alignment purposes - setting min-height: 40px; in the example does this.
http://www.bootply.com/GznfJxUWUF - remove the padding that is making the element calculate as taller (as mentioned in another answer).
Apostolos, the white space is coming from the .titleclass.
The bottom padding of 10px.
Zero this and the white space will go.
.title {
padding: 5px 5px 0px 5px;
you will have to add a float: left to both parent containers (.header-bar and breadcrumb) otherwise the clear won't affect anything. furthermore you will have to give both containers width: 100%
.header-bar {
border: None;
background-color: #66CCFF;
min-height:30px;
width: 100%;
float: left;
}
.breadcrumb {
width: 100%;
float: left;
}
.title {
padding: 5px 5px 10px 5px;
color: white;
font-size: large;
}
.clear-both{
clear:both;
}

HTML body margin 0 messing up div positioning

I am a bite confused on what is happening here. I put my body margin set to 0 in my css and then all the div elements stretch across the screen like I want, but I want this to apply for only one. From a previous question: HTML Image going across entire screen
An answer said to use position:absolute and then change the position of the div elements. I used to have position:relative on these div elements and when I changed that to absolute, it combined all the div elements in one position. I tried moving them with bottom:then whatever pixels, but still did not move it at all. Would this be the way to move it? What would I do? On W3 schools: http://www.w3schools.com/css/css_positioning.asp
It tells me a lot about positioning div elements, but when I tried to use this it did not work on one div element I tried, but instead overlapped it.
How would I move these div elements?
Code CSS
#middle-4{
position:absolute;
left:0;
right:8;
bottom:0;
top:-800px;}
Code HTML
<div id="middle-4" style="background-image: url(images/Home/rock.png); height: 540px; width: 1348px; border: 1px solid black;"></div>
This is done so for as you can see up to 4 div elements.
If I understand your question correctly you want all element to conform to the default body margin except one element (or multiple elements using a class).
I would do it like this...
Give body a specific margin to ensure it is consistent across browsers.
Use negative horizontal margins to pull your element outside of the constraints of body
body {
margin: 8px;
background: lightGreen;
}
div {
background: lightBlue;
padding: 30px;
border-bottom: 1px solid blue;
}
.fullwidth {
margin-left: -8px;
margin-right: -8px;
}
<div>I'm constrained by body</div>
<div class="fullwidth">I'm full width</div>
<div>I'm constrained by body</div>
Setting margin on body only ensures cross-browser consistency as mentioned by uʍopǝpısdn
If you have 4 divs containing an image each, you should stick to position: relative - this will line up the divs / images vertically on top of each other.
Your issue might have to do with image sizes - if you want all images to keep their original size, you can keep their attributes for width and height as specified in your example "middle-4": height: 540px; width: 1348px;
However - do you want one div / image to stretch across the width of body / screen, you will have to apply the size in percentage - this can be done in 2 ways:
CSS3 - you have the options of "cover" or "contain", which can be applied to div as youre doing it now - example:
div {
background: url(images/Home/rock.png);
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
CSS2 - you can apply a class to the image itself, and forget about the surrounding div - example:
<img src="images/Home/rock.png" class="img_width" />
.img_width {
width: 100%;
height: auto;
}

left v/s margin-left and CSS: position

I am playing around to make an HTML/CSS carousel.
HTML:
<body>
<div id="container">
<div id="wrapper">
<div id="d1" class="box"><p>DIV#1</p></div>
<div id="d2" class="box"><p>DIV#2</p></div>
<div id="d3" class="box"><p>DIV#3</p></div>
<div id="d4" class="box"><p>DIV#4</p></div>
</div>
</div>
</body>
CSS:
.box {
height: 100px;
width: 100px;
margin: 15px;
border: 2px solid black;
color: black;
float: left;
}
#container {
width: 150px;
height: 144px;
overflow: hidden;
border: 2px solid black;
}
#wrapper {
height: 140px;
width: 555px;
border: 2px solid green;
position: relative;
left: 0px;
}
#d1 {
background-color: blue;
}
#d2 {
background-color: red;
}
#d3 {
background-color: green;
}
#d4 {
background-color: yellow;
}
Here's the fiddle: http://jsfiddle.net/97jhB/.
I intend to add javascript controls and provisions for left/right buttons later.
First, I just want to learn conceptually how it works.
I am trying to get the carousel 'effect' by playing with the wrapper's left.
If I go on decreasing the wrapper's left, I will be able to see the boxes successively.
I have a couple of questions:
If I don't set the wrapper's position to relative, changes made to it's left do not take effect. Why is that so? Isn't the wrapper supposed to be relative by default?
If I play around with the wrapper's margin-left instead of left, it seems to work as desired.
What is better between these two approaches: playing with left or playing with margin-left?
Because only relative, absolute and fixed positioning use left, right, top, and bottom to define their locations relative to the current context they are in.
Fixed is relative to the viewport, absolute is taken out of the normal page flow and relative to the first parent with a CSS position set on it, and relative is just relative to the nearest block-level ancestor.
static is the default position and uses margin-left, margin-right, etc to position the element relative to other elements in the page flow, within the nearest block-level ancestor.
Also, be aware that position:fixed does not work as expected on older mobile devices.
MDN has great documentation on this subject.
When you assign the position:relative CSS declaration to a div, you're not actually moving the space it takes up on the page, just where it is displayed.
However the default position is static for any html element if not specified explicitly.
position: static;
Check out this link on SO for a very complete explanation of the margin-left v/s left difference
Difference between margin-left and left
Static is the default, and the best thing to do is to have the wrapper relative and the items absolute, this way overflowing items won't go to the bottom (~ won't create new lines)... You'll have to remove float:left if you want to follow this path.
It's probably better to use left (or right if RTL), what if you want some margin between that your carousel slides, think of the scenario where you have more than one visible item.

Why does 100% not mean 100% height?

I am trying to get some divss to expand to fill the screen, but I am struggling. I have broken down the issue on this jsfiddle.
What I really want to know is why does the div, with its 100% min-height, not expand to that height (or at all) when its parent has the same attribute and does expand?
<body>
<div>
stuff
</div>
</body>
body {
min-height: 100%;
background: red;
}
div {
min-height: 100%;
background: grey;
}
The issue is covered in the CSS 2.1 spec:
<percentage>
Specifies a percentage height. The percentage is
calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly (i.e., it depends on content height), and this
element is not absolutely positioned, the value computes to 'auto'. A
percentage height on the root element is relative to the initial
containing block. Note: For absolutely positioned elements whose
containing block is based on a block-level element, the percentage is
calculated with respect to the height of the padding box of that
element. This is a change from CSS1, where the percentage was always
calculated with respect to the content box of the parent element.
So, to clarify, a percentage height will reference the height of its containing block (unless it is position: absolute or position: fixed). If that containing block does not have a specified height, then the percentage will refer to auto, and it won't really do much.
position: absolute changes the referenced containing block to the nearest positioned (absolute, relative, or fixed) element.
position: fixed changes the referenced containing block to the viewport.
So, if you specify a height on your containing block, specify a position other than static on your containing block, or don't mind using the viewport as your containing block, then you can use percentage heights effectively.
Please see my demonstration at jsFiddle
You need to also set the height of the html so that 100% refers to the viewport height instead of the document height (demo):
html,body {
height: 100%;
background: red;
padding: 0;
}
div {
height: 100%;
background: grey;
}
because you can't really use 100% height on a static element. Changing the position attribute from static to absolute will give you 100% height. demo
posted as answer per the request of the the PO.
Percentage heights in CSS don't make a lot of sense to me. I would argue that it doesn't work the way it should, but CSS enthusiasts would insist that it does.
This article discusses both the issue and solution in detail:
http://matthewjamestaylor.com/blog/equal-height-columns-cross-browser-css-no-hacks
This might help too:
<style>
#outer {position:absolute; height:auto; width:200px; border: 1px solid red; }
#inner {position:absolute; height:100%; width:20px; border:1px solid black; }
</style>
<div id="outer">
<div id="inner"></div>
text
</div>
See here for more details on the above:
How to make a floated div 100% height of its parent?
There are two issues, you'll want to specify the height of the html as well, as in:
html, body {
min-height: 100%;
}
Also there appears to be an issue in IE where min-height doesn't do the trick for the div but specifying height on the div does the trick. As such:
html, body {
min-height: 100%;
background: red;
}
div {
height: 100%;
background: grey;
}
This will work
body, html {
height: 100vh;
}
aside {
background: green;
width: 200px;
height: 100vh;
}

unexpected margin with very simple html

I have a very simple html. The red div is inside the blue div and has a 10 px top margin. On non-ie browsers, the blue box is 10 px apart from the top of viewport and the red div is at the very top of the blue div. What I expect is the ie behavior: red div must be 10 px apart from the top of the blue div. Why does non-ie browsers render like this? (I suppose the wrong behavior is the IE's but why?)
And, what is the correct way to do this?
why blank? http://img92.imageshack.us/img92/7662/blankmr7.jpg
<html>
<head>
<style>
body { margin:0; padding:0; }
.outer
{
background-color: #00f;
height: 50px;
}
.inner
{
height: 20px;
width: 20px;
background-color: #f00;
margin: 10px 0 0 10px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner">
</div>
</div>
</body>
</html>
As much as strager's answer already explains about as much as you need to know as to why it happens – namely that it happens the way it does in browsers other than IE because the specs say so – I think he picked the wrong quote from the section of the CSS 2.1 specification about collapsing margins.
The point he quoted explains how margins can collapse, not how they can "move" to a parent element.
This is rather what explains it:
If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.
If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
Or, in slightly more human-readable form in the Mozilla developer documentation:
Parent and first/last child:
If there is no border, padding, inline content, or clearance to separate the margin-top of a block with the margin-top of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate the margin-bottom of a block with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.
As for how to fix it, I'd probably go for the overflow: auto solution Chris Lloyd suggested (as much as that may have side-effects).
But then that really depends on what exactly the rest of your code looks like. In this simple example you could easily just change the margin on the child element to a padding on the parent element.
Or you could float the child element, or absolutely position it...
Or how about an inverse clearfix if you want to get really fancy:
.outer:before {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
The margins are being merged. The output produced by IE is probably incorrect.
In the specifications (which are down for me at the moment):
Two or more adjoining vertical margins of block boxes in the normal flow collapse. The resulting margin width is the maximum of the adjoining margin widths.
You can use borders instead of margins, with border-color set to transparent.
There is a pretty fitting answer to this question: overflow: auto;
<html>
<head>
<style>
body { margin:0; padding:0; }
.outer
{
background-color: #00f;
height: 50px;
overflow: auto;
}
.inner
{
height: 20px;
width: 20px;
background-color: #f00;
margin: 10px 0 0 10px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner">
</div>
</div>
</body>
</html>
Could it be IE sees the DOM as div.inner having div.outer as it's parent node(and calculates offset from it),
and that other browsers instead has both of them answering to the body element?
Ok, solution without overflow auto:
<html>
<head>
<style>
body { margin:0; padding:0; }
.outer
{
background-color: #00f;
height: 50px;
border: 1px solid transparent;
}
.inner
{
height: 20px;
width: 20px;
background-color: #f00;
margin: 10px 0 0 10px;
padding: 0;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner">
</div>
</div>
</body>
</html>
The inner element is wanting something to push against, and providing a boder (or forcing the browser to consider the overflow) does this.