Why doesn't the absolute positioned div start from the top? - html

Consider the html and css in this codepen link.
#box2 in the page has been positioned absolute and has a left offset of 200px, so moves 200 px to the right. However, the top offset also gets set to 200px somehow, but if I correctly understand absolute positioning, it should be positioned relative to its parent (body in this case), and so it should have top offset 0.
Can you explain why this happens?

With your css .box you set all boxes 1 - 3 as position: relative.
Your css #box2 sets a absolute positioning on box 2. The css selector for an id (#box2) is more specific than a class selector (.box) and therefore positioning: absolute in #box2 overrules the positioning: relative in .box
You do not define a top property/value in your css for the #box2. That means it will flow with the other tags except for the left position.
Because a div is displayed as block it will display right below #box1. It will be drawn 200px from the left because of your css left: 200px;
Try to change your css for #box2 into something like this
#box_2 { background: #44accf; left: 150px; top: 100px; position: absolute; }
and play around with the left and top values, to understand what the absolute positioning is doing.

You haven't given it a top: value - so it's placing itself where it was if it was relatively positioned/default place (As BoltClock has said, this is known as the static position).
For the purposes of this section and the next, the term "static
position" (of an element) refers, roughly, to the position an element
would have had in the normal flow.
More precisely, the static position
for 'top' is the distance from the top edge of the containing block to
the top margin edge of a hypothetical box that would have been the
first box of the element if its specified 'position' value had been
'static' and its specified 'float' had been 'none' and its specified
'clear' had been 'none'.
~W3 Spec
Here is a basic example:
html,
body {
margin: 0;
padding: 0;
}
.box {
position: relative;
width: 200px;
height: 200px;
}
#box_1 {
background: #ee3e64;
}
#box_2 {
background: #44accf;
left: 200px;
position: absolute;
top: 0;
}
#box_3 {
background: #b7d84b;
}
<div id="box_1" class="box"></div>
<div id="box_2" class="box"></div>
<div id="box_3" class="box"></div>
As you can see, this would also be able to do this using the display:inline-block property (since you're removing the defaulted 'block' styling that take's up 100% of the width), which you then wouldn't need to worry about the absolute at all:
html,
body {
margin: 0;
padding: 0;
}
.box {
position: relative;
width: 200px;
height: 200px;
display: inline-block;
}
#box_1 {
background: #ee3e64;
}
#box_2 {
background: #44accf;
}
#box_3 {
background: #b7d84b;
}
<div id="box_1" class="box"></div>
<div id="box_2" class="box"></div>
<div id="box_3" class="box"></div>
If, however, you needed it to only have two squares wide, you might want to wrap it in a container div width and set a width:
html,
body {
margin: 0;
padding: 0;
}
.box {
position: relative;
width: 200px;
height: 200px;
display: inline-block;
}
#box_1 {
background: #ee3e64;
}
#box_2 {
background: #44accf;
}
#box_3 {
background: #b7d84b;
}
.container{
width:500px;
}
<div class="container">
<div id="box_1" class="box"></div>
<div id="box_2" class="box"></div>
<div id="box_3" class="box"></div>
</div>
So, you were right to think that the absolutely positioned element is aligning to the next available parent with position:relative (which, just so happens to be the body, since you haven't declared one), you have just missed the use of top to position it where you want vertically, and so is defaulting to where 'it would be otherwise' - which is the baseline (which is at the bottom by default in divs)

Related

How to have a details container be the same size as its content if the latter has position absolute? [duplicate]

As you can see in the CSS below, I want child2 to position itself before child1. This is because the site I'm currently developing should also work on mobile devices, on which the child2 should be at the bottom, as it contains the navigation which I want below the content on the mobile devices. - Why not 2 masterpages? This is the only 2 divs which are repositioned in the entire HTML, so 2 masterpages for this minor change is an overkill.
HTML:
<div id="parent">
<div class="child1"></div>
<div class="child2"></div>
</div>
CSS:
parent { position: relative; width: 100%; }
child1 { width: auto; margin-left: 160px; }
child2 { width: 145px; position: absolute; top: 0px; bottom: 0px; }
child2 has dynamic height, as different subsites could have more or less navigation items.
I know that absolute positioned elements are removed from the flow, thus ignored by other elements.
I tried setting overflow:hidden; on the parent div, but that didn't help, neither does the clearfix.
My last resort will be JavaScript to reposition the two divs accordingly, but for now I'll try and see if there exist a non-JavaScript way of doing this.
You answered the question yourself:
I know that absolute positioned elements are removed from the flow, thus ignored by other elements.
So you can't set the parents height according to an absolutely positioned element.
You either use fixed heights or you need to involve JavaScript.
Nowadays one might use CSS flexbox or grid layout to reverse the visual order of HTML elements inside a parent container without using position: absolute;. See also Reverse order of columns in CSS Grid Layout
Although stretching to elements with position: absolute is not possible, there are often solutions where you can avoid the absolute positioning while obtaining the same effect. Look at this fiddle that solves the problem in your particular case http://jsfiddle.net/gS9q7/
The trick is to reverse element order by floating both elements, the first to the right, the second to the left, so the second appears first.
.child1 {
width: calc(100% - 160px);
float: right;
}
.child2 {
width: 145px;
float: left;
}
Finally, add a clearfix to the parent and you're done (see the fiddle for the complete solution).
Generally, as long as the element with absolute position is positioned at the top of the parent element, chances are good that you find a workaround by floating the element.
There is a quite simple way to solve this.
You just have to duplicate the content of child1 and child2 in relative divs with display:none in parent div. Say child1_1 and child2_2. Put child2_2 on top and child1_1 at the bottom.
When your jquery (or whatever) calls the absolute div, just set the according relative div (child1_1 or child2_2) with display:block AND visibility:hidden. The relative child will still be invisible but will make parent's div higher.
Feeela is right but you can get a parent div contracting or expanding to a child element if you reverse your div positioning like this:
.parent {
position: absolute;
/* position it in the browser using the `left`, `top` and `margin`
attributes */
}
.child {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
/* to pad or move it around using `left` and `top` inside the parent */
}
This should work for you.
This question was asked in 2012 before flexbox. The correct way to solve this problem using modern CSS is with a media query and a flex column reversal for mobile devices. No absolute positioning is needed.
https://jsfiddle.net/tnhsaesop/vjftq198/3/
HTML:
<div class="parent">
<div style="background-color:lightgrey;">
<p>
I stay on top on desktop and I'm on bottom on mobile
</p>
</div>
<div style="background-color:grey;">
<p>
I stay on bottom on desktop and I'm on top on mobile
</p>
</div>
</div>
CSS:
.parent {
display: flex;
flex-direction: column;
}
#media (max-width: 768px) {
.parent {
flex-direction: column-reverse;
}
}
With pure JavaScript, you just need to retrieve the height of your static position child element .child1 using the getComputedStyle() method then set that retrieve value as the padding-top for that same child using the HTMLElement.style property.
Check and run the following Code Snippet for a practical example of what I described above:
/* JavaScript */
var child1 = document.querySelector(".child1");
var parent = document.getElementById("parent");
var childHeight = parseInt(window.getComputedStyle(child1).height) + "px";
child1.style.paddingTop = childHeight;
/* CSS */
#parent { position: relative; width: 100%; }
.child1 { width: auto; }
.child2 { width: 145px; position: absolute; top: 0px; bottom: 0px; }
html, body { width: 100%;height: 100%; margin: 0; padding: 0; }
<!-- HTML -->
<div id="parent">
<div class="child1">STATIC</div>
<div class="child2">ABSOLUTE</div>
</div>
There's a very simple hack that fixes this issue
Here's a codesandbox that illustrates the solution: https://codesandbox.io/s/00w06z1n5l
HTML
<div id="parent">
<div class="hack">
<div class="child">
</div>
</div>
</div>
CSS
.parent { position: relative; width: 100%; }
.hack { position: absolute; left:0; right:0; top:0;}
.child { position: absolute; left: 0; right: 0; bottom:0; }
you can play with the positioning of the hack div to affect where the child positions itself.
Here's a snippet:
html {
font-family: sans-serif;
text-align: center;
}
.container {
border: 2px solid gray;
height: 400px;
display: flex;
flex-direction: column;
}
.stuff-the-middle {
background: papayawhip
url("https://camo.githubusercontent.com/6609e7239d46222bbcbd846155351a8ce06eb11f/687474703a2f2f692e696d6775722e636f6d2f4e577a764a6d6d2e706e67");
flex: 1;
}
.parent {
background: palevioletred;
position: relative;
}
.hack {
position: absolute;
left: 0;
top:0;
right: 0;
}
.child {
height: 40px;
background: rgba(0, 0, 0, 0.5);
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
<div class="container">
<div class="stuff-the-middle">
I have stuff annoyingly in th emiddle
</div>
<div class="parent">
<div class="hack">
<div class="child">
I'm inside of my parent but absolutely on top
</div>
</div>
I'm the parent
<br /> You can modify my height
<br /> and my child is always on top
<br /> absolutely on top
<br /> try removing this text
</div>
</div>
I came up with another solution, which I don't love but gets the job done.
Basically duplicate the child elements in such a way that the duplicates are not visible.
<div id="parent">
<div class="width-calc">
<div class="child1"></div>
<div class="child2"></div>
</div>
<div class="child1"></div>
<div class="child2"></div>
</div>
CSS:
.width-calc {
height: 0;
overflow: hidden;
}
If those child elements contain little markup, then the impact will be small.
I had a similar problem.
To solve this (instead of calculate the iframe's height using the body, document or window) I created a div that wraps the whole page content (a div with an id="page" for example) and then I used its height.
"You either use fixed heights or you need to involve JS."
Here is the JS example:
---------- jQuery JS example--------------------
function findEnvelopSizeOfAbsolutelyPositionedChildren(containerSelector){
var maxX = $(containerSelector).width(), maxY = $(containerSelector).height();
$(containerSelector).children().each(function (i){
if (maxX < parseInt($(this).css('left')) + $(this).width()){
maxX = parseInt($(this).css('left')) + $(this).width();
}
if (maxY < parseInt($(this).css('top')) + $(this).height()){
maxY = parseInt($(this).css('top')) + $(this).height();
}
});
return {
'width': maxX,
'height': maxY
}
}
var specBodySize = findEnvelopSizeOfAbsolutelyPositionedSubDivs("#SpecBody");
$("#SpecBody").width(specBodySize.width);
$("#SpecBody").height(specBodySize.height);
There is a better way to do this now. You can use the bottom property.
.my-element {
position: absolute;
bottom: 30px;
}
This is very similar to what #ChrisC suggested. It is not using an absolute positioned element, but a relative one. Maybe could work for you
<div class="container">
<div class="my-child"></div>
</div>
And your css like this:
.container{
background-color: red;
position: relative;
border: 1px solid black;
width: 100%;
}
.my-child{
position: relative;
top: 0;
left: 100%;
height: 100px;
width: 100px;
margin-left: -100px;
background-color: blue;
}
https://jsfiddle.net/royriojas/dndjwa6t/
Also consider next approach:
CSS:
.parent {
height: 100%;
}
.parent:after {
content: '';
display: block;
}
Also since you are trying to reposition divs consider css grid
Absolute views position themselves against the nearest ancestor that isn't statically positioned (position: static), therefore if you want an absolute view positioned against a given parent, set the parent position to relative and the child to position to absolute
Try this, it was worked for me
.child {
width: 100%;
position: absolute;
top: 0px;
bottom: 0px;
z-index: 1;
}
It will set child height to parent height

CSS relative position with right side relative to viewport

I have several divs, each a child of the previous. They're all positioned relative, so that I can offset the children relative to the parents. I want to have the right sides line up, but I can't figure out how. Doing width: 100% obviously doesn't work because it ignores that the divs are not at left: 0.
As you can see, the divs escape the viewport:
div {
position: relative;
width: 100%;
height: 250px;
left: 50px;
top: 50px;
}
#div1 {
background-color: #4286f4;
}
#div2 {
background-color: #3267bc;
}
#div3 {
background-color: #244a87;
}
<div id="div1">
<div id="div2">
<div id="div3">
</div>
</div>
</div>
Or if you prefer: JSFiddle
How can I position the right side of each div at some specific distance from the right side of the screen with only CSS?
This is the expected output (the black rectangle around the outside represents the edges of the screen):
You might consider using margin-left instead of left.
With the CSS box model default, "width and height properties include the content, but does not include the padding, border, or margin," (box-sizing).
Also, block-level elements take up "the full width available", so width:100% isn't necessary (Block-level elements).
body {
margin: 20px 60px 20px 20px;
}
div {
position: relative;
}
div div {
height: 80px;
margin-left: 40px;
top: 40px;
}
#div1 {
background-color: #4286f4;
}
#div2 {
background-color: #3267bc;
}
#div3 {
background-color: #244a87;
}
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>

Setting an absolute positioned div to overlap one regular div but not another

I have a page that's set up with a split pane - top and bottom (divs using calc() for their height), with the top div having an absolutely positioned div that overlays the top div. HTML + CSS below from https://jsfiddle.net/b2mb79ev/1/
<div id="everything">
<div id="top">
<div id="free"></div>
</div>
<div id="bottom"></div>
</div>
</div>
#everything {
width: 400px;
height: 400px;
}
#top {
height: calc(50%);
width: 100%;
background: blue;
}
#free {
position: absolute;
top: 50px;
left: 300px;
height: 200px;
width: 50px;
background: yellow;
border: 3px solid black;
}
#bottom {
height: calc(50%);
width: 100%;
background: rgba(0,255,0,0.6);
}
However I want the absolutely positioned div not to intrude into the bottom div. I want the bottom div to always be in a 'higher' layer. In the jsfiddle I don't want the yellow bit going into the green bit.
But from what I can gather the top and bottom divs always have the same level because they are just regularly flowed elements and won't take any notice of z-index. I can use z-index to have the absolutely positioned div above both or beneath both, but not one above and one underneath?
Is there a way to do what I'd like?
You just need to set a non-static position (e.g. position:relative) on the #bottom div to give the stacking context you're after.
jsFiddle example
#bottom {
height: calc(50%);
width: 100%;
background: rgba(0, 255, 0, 0.6);
position:relative;
}

Css: Position element by it's bottom relative to it's container's top

I have an div element with variable height which I need to be positioned by it's bottom relative to the containers top.
This must be done without changing the html.
e.g.
<div id="container">
<h1>Some Text<br/>more...</h1>
</div>
h1's bottom should be 100px below #container's top.
Thanks a lot
EDIT:
So by Request what I did (or didn't) tried:
Searching with Google for css bottom top position relative but that's not the best search terms in the world...
Normally I would put a container around h1 and give it a height of 100px but then I would need to change the html and that I can't
using bottom: somevalue but that positions the element's bottom relative to the container's bottom.
slain some vampires
You could make use of transform: translateY(-100%), to make the bottom of the element relative when you apply margin-top: 100px to h1.
#container {
width: 200px;
height: 200px;
background: tan;
overflow: hidden;
}
#container h1 {
transform: translateY(-100%);
margin-top: 100px;
background: papayawhip
}
<div id="container">
<h1>Some Text<br/>more...</h1>
</div>
Depending on browser support requirements:
#container {
position: relative;
}
#container h1 {
position: absolute;
bottom: calc(100% - 100px);
}
Example
Only way through it is to add a height to the h1 unless you want to go with calc which isn't supported yet by some browsers. Then set your top margin to be top: 100px - h1's height. Hope this works
<div id="container">
<h1>Some Text<br/>more...</h1>
</div>
#container {
width: 300px;
height: 300px;
background: #222;
overflow: hidden;
}
#container h1 {
background: #444;
position:relative;
height:80px;
top:20px;
}
http://jsfiddle.net/ms889w57/
#container
{
position: absolute;
height: 100px;
bottom:0px;
}
This code is not affecting html at all. I added css for id-container.
An absolute position element is positioned relative to the first parent element that has a position other than static. You can change it to fixed it you wants to.
Height of the container, help you to calculate spacing from bottom.

Make absolute positioned div expand parent div height

As you can see in the CSS below, I want child2 to position itself before child1. This is because the site I'm currently developing should also work on mobile devices, on which the child2 should be at the bottom, as it contains the navigation which I want below the content on the mobile devices. - Why not 2 masterpages? This is the only 2 divs which are repositioned in the entire HTML, so 2 masterpages for this minor change is an overkill.
HTML:
<div id="parent">
<div class="child1"></div>
<div class="child2"></div>
</div>
CSS:
parent { position: relative; width: 100%; }
child1 { width: auto; margin-left: 160px; }
child2 { width: 145px; position: absolute; top: 0px; bottom: 0px; }
child2 has dynamic height, as different subsites could have more or less navigation items.
I know that absolute positioned elements are removed from the flow, thus ignored by other elements.
I tried setting overflow:hidden; on the parent div, but that didn't help, neither does the clearfix.
My last resort will be JavaScript to reposition the two divs accordingly, but for now I'll try and see if there exist a non-JavaScript way of doing this.
You answered the question yourself:
I know that absolute positioned elements are removed from the flow, thus ignored by other elements.
So you can't set the parents height according to an absolutely positioned element.
You either use fixed heights or you need to involve JavaScript.
Nowadays one might use CSS flexbox or grid layout to reverse the visual order of HTML elements inside a parent container without using position: absolute;. See also Reverse order of columns in CSS Grid Layout
Although stretching to elements with position: absolute is not possible, there are often solutions where you can avoid the absolute positioning while obtaining the same effect. Look at this fiddle that solves the problem in your particular case http://jsfiddle.net/gS9q7/
The trick is to reverse element order by floating both elements, the first to the right, the second to the left, so the second appears first.
.child1 {
width: calc(100% - 160px);
float: right;
}
.child2 {
width: 145px;
float: left;
}
Finally, add a clearfix to the parent and you're done (see the fiddle for the complete solution).
Generally, as long as the element with absolute position is positioned at the top of the parent element, chances are good that you find a workaround by floating the element.
There is a quite simple way to solve this.
You just have to duplicate the content of child1 and child2 in relative divs with display:none in parent div. Say child1_1 and child2_2. Put child2_2 on top and child1_1 at the bottom.
When your jquery (or whatever) calls the absolute div, just set the according relative div (child1_1 or child2_2) with display:block AND visibility:hidden. The relative child will still be invisible but will make parent's div higher.
Feeela is right but you can get a parent div contracting or expanding to a child element if you reverse your div positioning like this:
.parent {
position: absolute;
/* position it in the browser using the `left`, `top` and `margin`
attributes */
}
.child {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
/* to pad or move it around using `left` and `top` inside the parent */
}
This should work for you.
This question was asked in 2012 before flexbox. The correct way to solve this problem using modern CSS is with a media query and a flex column reversal for mobile devices. No absolute positioning is needed.
https://jsfiddle.net/tnhsaesop/vjftq198/3/
HTML:
<div class="parent">
<div style="background-color:lightgrey;">
<p>
I stay on top on desktop and I'm on bottom on mobile
</p>
</div>
<div style="background-color:grey;">
<p>
I stay on bottom on desktop and I'm on top on mobile
</p>
</div>
</div>
CSS:
.parent {
display: flex;
flex-direction: column;
}
#media (max-width: 768px) {
.parent {
flex-direction: column-reverse;
}
}
With pure JavaScript, you just need to retrieve the height of your static position child element .child1 using the getComputedStyle() method then set that retrieve value as the padding-top for that same child using the HTMLElement.style property.
Check and run the following Code Snippet for a practical example of what I described above:
/* JavaScript */
var child1 = document.querySelector(".child1");
var parent = document.getElementById("parent");
var childHeight = parseInt(window.getComputedStyle(child1).height) + "px";
child1.style.paddingTop = childHeight;
/* CSS */
#parent { position: relative; width: 100%; }
.child1 { width: auto; }
.child2 { width: 145px; position: absolute; top: 0px; bottom: 0px; }
html, body { width: 100%;height: 100%; margin: 0; padding: 0; }
<!-- HTML -->
<div id="parent">
<div class="child1">STATIC</div>
<div class="child2">ABSOLUTE</div>
</div>
There's a very simple hack that fixes this issue
Here's a codesandbox that illustrates the solution: https://codesandbox.io/s/00w06z1n5l
HTML
<div id="parent">
<div class="hack">
<div class="child">
</div>
</div>
</div>
CSS
.parent { position: relative; width: 100%; }
.hack { position: absolute; left:0; right:0; top:0;}
.child { position: absolute; left: 0; right: 0; bottom:0; }
you can play with the positioning of the hack div to affect where the child positions itself.
Here's a snippet:
html {
font-family: sans-serif;
text-align: center;
}
.container {
border: 2px solid gray;
height: 400px;
display: flex;
flex-direction: column;
}
.stuff-the-middle {
background: papayawhip
url("https://camo.githubusercontent.com/6609e7239d46222bbcbd846155351a8ce06eb11f/687474703a2f2f692e696d6775722e636f6d2f4e577a764a6d6d2e706e67");
flex: 1;
}
.parent {
background: palevioletred;
position: relative;
}
.hack {
position: absolute;
left: 0;
top:0;
right: 0;
}
.child {
height: 40px;
background: rgba(0, 0, 0, 0.5);
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
<div class="container">
<div class="stuff-the-middle">
I have stuff annoyingly in th emiddle
</div>
<div class="parent">
<div class="hack">
<div class="child">
I'm inside of my parent but absolutely on top
</div>
</div>
I'm the parent
<br /> You can modify my height
<br /> and my child is always on top
<br /> absolutely on top
<br /> try removing this text
</div>
</div>
I came up with another solution, which I don't love but gets the job done.
Basically duplicate the child elements in such a way that the duplicates are not visible.
<div id="parent">
<div class="width-calc">
<div class="child1"></div>
<div class="child2"></div>
</div>
<div class="child1"></div>
<div class="child2"></div>
</div>
CSS:
.width-calc {
height: 0;
overflow: hidden;
}
If those child elements contain little markup, then the impact will be small.
I had a similar problem.
To solve this (instead of calculate the iframe's height using the body, document or window) I created a div that wraps the whole page content (a div with an id="page" for example) and then I used its height.
"You either use fixed heights or you need to involve JS."
Here is the JS example:
---------- jQuery JS example--------------------
function findEnvelopSizeOfAbsolutelyPositionedChildren(containerSelector){
var maxX = $(containerSelector).width(), maxY = $(containerSelector).height();
$(containerSelector).children().each(function (i){
if (maxX < parseInt($(this).css('left')) + $(this).width()){
maxX = parseInt($(this).css('left')) + $(this).width();
}
if (maxY < parseInt($(this).css('top')) + $(this).height()){
maxY = parseInt($(this).css('top')) + $(this).height();
}
});
return {
'width': maxX,
'height': maxY
}
}
var specBodySize = findEnvelopSizeOfAbsolutelyPositionedSubDivs("#SpecBody");
$("#SpecBody").width(specBodySize.width);
$("#SpecBody").height(specBodySize.height);
There is a better way to do this now. You can use the bottom property.
.my-element {
position: absolute;
bottom: 30px;
}
This is very similar to what #ChrisC suggested. It is not using an absolute positioned element, but a relative one. Maybe could work for you
<div class="container">
<div class="my-child"></div>
</div>
And your css like this:
.container{
background-color: red;
position: relative;
border: 1px solid black;
width: 100%;
}
.my-child{
position: relative;
top: 0;
left: 100%;
height: 100px;
width: 100px;
margin-left: -100px;
background-color: blue;
}
https://jsfiddle.net/royriojas/dndjwa6t/
Also consider next approach:
CSS:
.parent {
height: 100%;
}
.parent:after {
content: '';
display: block;
}
Also since you are trying to reposition divs consider css grid
Absolute views position themselves against the nearest ancestor that isn't statically positioned (position: static), therefore if you want an absolute view positioned against a given parent, set the parent position to relative and the child to position to absolute
Try this, it was worked for me
.child {
width: 100%;
position: absolute;
top: 0px;
bottom: 0px;
z-index: 1;
}
It will set child height to parent height