Absolute element overflowing the document

Viewed 476

I came across a strange issue while trying to position an position: absolute popup in my project.

Regarding position: absolute MDN says:

The element is removed from the normal document flow, and no space is created for the element in the page layout.

Unfortunately, in the example below positioning the element with right: 0 and transform: translateX(100%) creates additional space outside the body as if the element were to be position: relative. This doesn't happen if we do left: 0 and transform: translateX(-100%).

  .parent {
    width: 100%;
    height: 100vh;
    border: 1px solid red;
    background-color: magenta;
  }
  .child {
    position: absolute;
    transform: translateX(100%);
    width: 100px;
    height: 50%;
    border: 1px solid black;
    right: 0;
    background-color: yellow;
  }
  <div class="parent">
     <div class="child"></div>
  </div>

Here's a link to an example of the issue https://jsbin.com/fikonusayi/edit?html,css,output

Is this a browser bug or is this an (un)expected feature?

P.S. Discovered in Chrome and Safari.

1 Answers

The issue here is about overflow not document flow. If we refer to the specification:

Generally, the content of a block box is confined to the content edges of the box. In certain cases, a box may overflow, meaning its content lies partly or entirely outside of the box, e.g.:

...

A descendant box is positioned absolutely, partly outside the box. Such boxes are not always clipped by the overflow property on their ancestors; specifically, they are not clipped by the overflow of any ancestor between themselves and their containing block

....

You may notice that there is no positioned element other than child so the containing block of the child will be the viewport. And the trick here is that the scroll bar is always added to the containing block starting from the left (horizontal scroll) or the top (vertical scroll) so any overflow from the top or the left will becomes inaccessible.

Here is an illustration to better see the issue:

.parent {
  width: 100%;
  height: 100vh;
  border: 1px solid red;
  background-color: magenta;
  box-sizing:border-box;
}

.child {
  position: absolute;
  width: 100px;
  height: 50%;
  border: 1px solid black;
  left: 0;
  background-color: yellow;
  animation:move 3s infinite linear alternate;
}
@keyframes move{
  from {left:-100px}
  to {left:100%}
}

body {
 margin:0;
}
<div class="parent">
  <div class="child"></div>
</div>

As you can see we have a scroll only when the element overflow on the left side. Same logic if we consider top/bottom

.parent {
  width: 100%;
  height: 100vh;
  border: 1px solid red;
  background-color: magenta;
  box-sizing:border-box;
}

.child {
  position: absolute;
  width: 100px;
  height: 50%;
  border: 1px solid black;
  left: 0;
  background-color: yellow;
  animation:move 3s infinite linear alternate;
}
@keyframes move{
  from {top:-100px}
  to {top:100%}
}

body {
 margin:0;
}
<div class="parent">
  <div class="child"></div>
</div>

And to confirm that your element is indeed removed from the flow, simply add content and remove the fixed height from the parent element and you will see that there is no space considered for your element:

.parent {
  border: 1px solid red;
  background-color: magenta;
  position: relative;
  box-sizing:border-box;
}

.child {
  position: absolute;
  width: 100px;
  height: 50%;
  border: 1px solid black;
  top:0;
  left: 0;
  background-color: yellow;
  animation:move 3s infinite linear alternate;
}
@keyframes move{
  from {left:-100px;top:-100px;}
  to {left:100%;top:100%;}
}

body {
 margin:0;
 border:2px solid red;
}
<div class="parent">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eget maximus nibh. Nam non libero molestie, placerat lorem sed, congue arcu. Aliquam eu ultrices nisi, sed facilisis purus. Suspendisse sit amet tincidunt massa, varius tempor mi. Suspendisse semper finibus ipsum in varius. Maecenas id commodo mi, vitae molestie diam. Nulla a risus cursus, auctor ligula sit amet, vestibulum purus. In in turpis non mi auctor viverra porta ac magna.
  <div class="child"></div>
</div>

Unlike with position:relative:

.parent {
  border: 1px solid red;
  background-color: magenta;
  position: relative;
  box-sizing:border-box;
}

.child {
  position: relative;
  width: 100px;
  height: 50px;
  border: 1px solid black;
  top:0;
  left: 0;
  background-color: yellow;
  animation:move 3s infinite linear alternate;
}
@keyframes move{
  from {left:-100px;top:-100px;}
  to {left:100%;top:100px;}
}

body {
 margin:0;
 border:2px solid red;
}
<div class="parent">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eget maximus nibh. Nam non libero molestie, placerat lorem sed, congue arcu. Aliquam eu ultrices nisi, sed facilisis purus. Suspendisse sit amet tincidunt massa, varius tempor mi. Suspendisse semper finibus ipsum in varius. Maecenas id commodo mi, vitae molestie diam. Nulla a risus cursus, auctor ligula sit amet, vestibulum purus. In in turpis non mi auctor viverra porta ac magna.
  <div class="child"></div>
</div>

Why it doesn't happen with position:fixed? simply because there is nothing specified in the specification. Here is all the cases where we have overflow:

  • A line cannot be broken, causing the line box to be wider than the block box.
  • A block-level box is too wide for the containing block. This may happen when an element's 'width' property has a value that causes the generated block box to spill over sides of the containing block.
  • An element's height exceeds an explicit height assigned to the containing block (i.e., the containing block's height is determined by the 'height' property, not by content height).
  • A descendant box is positioned absolutely, partly outside the box. Such boxes are not always clipped by the overflow property on their ancestors; specifically, they are not clipped by the overflow of any ancestor between themselves and their containing block
  • A descendant box has negative margins, causing it to be positioned partly outside the box. The 'text-indent' property causes an inline box to hang off either the left or right edge of the block box.
Related