How is Apple applying overflow:hidden to elements inside of position:fixed? - html

Based on my own tests and researching the topic on the internet, elements with position: fixed don't respect a parent element's overflow: hidden property. This includes children of the fixed position element — because they are within the fixed positioned element, they don't respect the ancestor's overflow: hidden property either.
However, Apple is somehow getting around this on Apple Music
Containing element with overflow: hidden
Inner element with position: fixed
The phone itself
You can see by the blue highlight in the second image that the fixed positioned element does indeed overflow its container, as does the phone inside it (3rd image). Yet, the contents of the fixed positioned element (the phone) still gets clipped by the ancestor with overflow: hidden. Using Chrome's dev tools, if I remove the overflow: hidden property from that ancestor, the entire phone indeed appears.
How is Apple doing this? I have tried recreating it with the same combination of fixed, relative, overflow, etc. settings, but I'm clearly missing something because I haven't been successful.
UPDATE
As #user3790069 points out in his answer, a fixed positioned element can be clipped by a relatively positioned one if the relatively positioned element has a higher z-index, or simply comes after the fixed position one (and is thus given a higher z-index by default). However, in the Apple Music example, the overflow: hidden property still seems to be the key they're relying on. To test this, I removed a bunch of the DOM to reduce clutter. What we're left with in the following screenshots are:
a) a containing section element with overflow: hidden
b) inside of that, a position: fixed element.
In the first screenshot, you'll see that the phone is clipped. In the second screenshot, I removed the overflow: hidden property and suddenly the overflowing part of the phone becomes visible.

First of all, I'm going to explain why aren't fixed children clipped by overflow: hidden on their parent. A box B is clipped by overflow applied to its ancestor A only if A is the containing block of B. But the containing block of a fixed element isn't a simple ancestor; a containing block of something with position: fixed is the whole wiewport.
I think that the fixed elements on the linked page aren't really cut by overflow; they're rather hidden under other positioned elements.
An exemple:
<div id=cont>
<div style="background: red">
<div class=fixed></div>
</div>
<div style="background: yellow">
<div class=fixed id=second></div>
</div>
<div style="background: green"></div>
</div>
#cont {
height: 5000px
}
#cont > div {
height: 200px;
position: relative
}
.fixed {
width: 100px;
height: 100px;
background: black;
position: fixed;
right: 20px;
top: 20px
}
#second {
top: 240px
}

Related

Why does a child of an absolutely positioned element not expand its parent width on overflow-y?

I have an absolutely positioned element attached to the body of a page. It does not have limitations on its height or width. I have child of the absolute element that contains a list and it's height is limited on the y-axis. This listing can be variable in length and width so I would prefer not to use any hard-set paddings or margins nor "overflow-y: scroll" because the scroll bar will show even when not needed.
<style>
.the-absolute {
display: block;
position: absolute;
}
.the-list {
border: 1px solid blue;
display: flex;
flex-direction: column;
max-height: 100px; /* arbitrary limit for example */
overflow-y: auto;
white-space: nowrap;
}
</style>
<body>
<div class="the-absolute">
<div class="the-list">
<div>Title</div>
<div>Year</div>
<div>Studio</div>
<div>Worldwide</div>
<div>Domestic</div>
<div>Budget</div>
<div>Title</div>
<div>Year</div>
<div>Studio</div>
<div>Worldwide</div>
<div>Domestic</div>
<div>Budget</div>
</div>
</div>
</body>
I would expect the width of the child element to expand the parent width while accommodating the scroll bar. Instead, the content of the longest list items is overlapped by the scroll bar on the right.
This works as I would expect in Chrome, but does not seem to play nice in IE11 (surprise!), Firefox or Safari.
I am a bit baffled here and would appreciate a better understanding of how absolute positioning affects the children of an element and if there is a way I can have dynamic (i.e. no hard-set margins, widths, etc) list that will be scrollable if it hits a certain threshold without overlapping the content.
I have tried multiple iterations and wrapping elements but something about the absolute positioning causes this. I can hack it using JS but would prefer a pure CSS solution. I just assume there is some detail I am missing or I lack the right combination of keywords to find the solution via google/stackoverflow.
All help is appreciated!
Absolutely-positioned elements are no longer part of the document flow, so they aren't really "in" their parent element anymore. They therefore do not affect the parent's dimensions.

Absolutely positioned elements with scrolling containers

An absolute positioned element is supposed to be removed from the element flow, and (so I understand) not able to make the document larger (creating scrollbars) but just go out of sight and out of mind.
But in my experiment when I offset an absolute element to the left I get scrollbars and to the right I get the expected behaviour. Why does it do this, and how could I get the behaviour I was expecting?
http://jsbin.com/bosajigapifu/6/edit
If you put the positioned absolute element into a container that is width: 100%, but overflow: hidden you can "push it into the void" that way. As long as its container does not go outside of the realm of the window it will not show scroll bars.
Elements:
<div id="container">
<div id="absolute"></div>
</div>
Styles:
<style>
#container{
width: 100%;
overflow: hidden;
}
#absolute{
position: absolute;
}
</style>
Absolutely positioned elements don't push other elements, but it does push out scroll-boxes if the element it's positioned relative to is the nearest scrolling ancestor.
The idea is that it's content and should be displayed. Kind of annoying tho when you want to position something outside a scroll container.
You may use position: fixed instead of absolute.
The diference is that it is not subject to any parent element, only to the viewport itself, but it does not create scrollbars when offset...
http://jsfiddle.net/t6g4421a/

Why do absolute elements stack up on each other instead of stacking one after the other?

How can get both #row1 and #row2 in the following code to be visible, one after the other vertically, as if there wasn't any absolute/relative positioning involved (though without removing the positioning properties)? I.e. having the two .row <div> to appear as "normal" block elements.
body { position:relative; min-height: 2em; width: 100%; }
.container {position:absolute;}
.row {position:relative;}
.col1, .col2 {position: absolute;}
<body>
<div class="container">
<div id="row1" class="row">
<div class="col1">Hello</div>
<div class="col2">World</div>
</div>
<div id="row2" class="row">
<div class="col1">Salut</div>
<div class="col2">le monde</div>
</div>
</div>
</body>
(Sample also available as a fiddle.)
I need the elements to have the positioning provided in the CSS rules, for reasons excluded here.
The content is programmatically dynamic; I don't know the elements' heights beforehand, so a solution can't be based on specifying an absolute length (e.g. 'px') anywhere.
Well you have some weird wishes here so let me explain you what those positions really mean in CSS and how they work, using position: relative; is just like using static position, the difference is making an element position: relative;, you will be able to use top, right, bottom and left properties, though the element will move, but physically it will be in the document flow..
Coming to position: absolute;, when you make any element position: absolute;, it gets out of the document flow, hence, it has nothing to do with any other element, so in your example
you have .col1, .col2 {position: absolute;} which are positioned absolute and since both are out of the document flow, they will overlap... Because they are already nested under position: absolute; parent i.e .container and since no width is assigned, it will take the minimal width and hence, your elements overlap, if you cannot change your CSS(which according to me doesn't make any sense why you can't change) still if you want, than you can do is this..
Demo (Without removing any of your position property) And this is really dirty
For the s characters, it will be at the top as your container element is out of the flow, and hence, no height will be considered in the document flow, unless and until you wrap that s in some element, and bring it down with, margin padding or CSS Positioning.
CSS Positions Explained
As I commented, here are few examples of how CSS Positioning actually works, to start with, there are 4 values for position property i.e static which is the default one, relative, absolute and fixed, so starting with static, nothing to learn much here, elements just stackup one below the other unless they are floated or made display: inline-block. With static positioning, top, right, bottom and left won't work.
Demo
Coming to position: relative; I've already explained you in general, it's nothing but same as static, it stacks up on other element, it is in the document flow, but you can tweak the elements position using top, right, bottom and left, physically, the element stays in the flow, only position of the element is changed.
Demo 2
Now comes absolute which generally many fails to understand, when making an element absolute it gets out of the document flow, and hence it stays independent, it has nothing to do with other elements positioning unless it's overlapped by other position: absolute element which can be fixed using z-index to change the stack level. The main thing to remember here is to have a position: relative; container so that your absolute positioned element is relative to that relative positioned element, else your element will fly out in the wild.
It's worth noting that position: absolute; element when positioned absolute; inside an absolute positioned parent element, than it is relative to that element and not relative to the grand parent element which may be positioned relative
Demo 3 (Without position: relative; container)
Demo 4 (With position: relative; container)
Last is position fixed, this is same as absolute but it flows along when you scroll, it's out of the document flow, but it scrolls, also, position: fixed; element cannot be relative to any container element having any type of position, not even relative, position fixed element is always relative to the viewport, so designers use position: absolute; when they want to have a fixed position behavior but relative to parent and tweak the top property onScroll.
Demo 5
What you want, is not possible without modifying the CSS position property. However, what you can do without touching the existing CSS, is overriding it with a more specific selector
.row .col1, .row .col2 {
position: relative;
}
See JSFiddle
when position:relative is used, the page layout will occur normally before being offset by top, left values, however position:absolute will ignore the document flow. The relative ones will work with no changes but absolute must be changed
.col1, .col2 {display:inline-block;}
http://jsfiddle.net/C4bQN/
EDIT: Depending on your circumstances, maybe you can wrap your table in an absolute positioned div then use normal document flow within the table?
<div class="absolute-wrap">
<div class="row">
<div class="col"> </div>
</div>
</div>

Children element not stretch parent container

Children element not stretch parent container.
My code:
html:
<div class='window'>
<div class='wrapper'>
<div class='short'>short</div>
<div class='long'>long</div>
</div>
</div>
css:
.window{
width: 500px;
height: 100px;
overflow: auto;
background: gray;
}
.wrapper{
background: pink;
border: 1px solid red;
}
.long{
width: 700px;
background: beige;
}
example
I want .long stretch his parent .wrapper.
.wrapper width must be the same as .long (700px).
I can reach this by setting float: left to .wrapper.
But what happens here i don't understand, why it helps? What is the mechanism of such behavior? I need explanation, with links to w3c documentation.
What else can i do to extend .wrapper width?
By default, the .wrapper div is inheriting the fixed width you set on .window. You can float the .wrapper and set it's width to auto so the width expands without restriction to the parent.
CSS:
.wrapper {
float: left;
width: auto;
}
Example: http://jsfiddle.net/WTGAc/3/
Theory:
By default, the dimensions of wrapper are constained to the dimensions placed on it's parent, .window.
Floated elements still live within the parameters defined by their
parent element, ie the width available and horizontal position. They
still interact with text and other elements inside that parent element
(or other following elements). In that respect, they are quite
different from absolutely positioned elements, which are removed from
the document flow and don't interact with other elements ... but even
then, if they have a positioned ancestor then they are restricted by
the envelope of that ancestor and will use that as the basis for
calculating size and dimension (although they can still be made to
extend or exist outside that positioned ancestor).
Source of Quote
Since the element is floated and set outside of the normal document flow, it can now expand to the true width of the parent, instead of the fixed width initially defined.
Widths and the CSS Visual Formatting Model
In you example, you have the following:
<div class='window'>
<div class='wrapper'>
<div class='short'>short</div>
<div class='long'>long</div>
</div>
</div>
In the simplest case, .window is the containing block with a fixed width (500px). The child element .wrapper inherits the width from .window. The .long element has a width of 700px and it will trigger an overflow condition. Since .window has overflow: auto declared, the .window element will generate a horizontal scroll bar. Note that by using overflow: auto, .window establishes a new block formatting context, which is why the horizontal scroll bar appears on .window instead of the viewport/root element.
When you float .wrapper to the left, the .wrapper element defines an additional block formatting context. A new block formatting context will ignore the width inherited from its containing block and computes a new width sufficient to enclose the content (shrink-to-fit), which is why the pink background from .wrapper now extends the entire 700px in width.
You can trigger the same effect by adding position: relative to .window and position: absolute to .wrapper. However, this works because you set the height to .window, otherwise, the .window height would compute to zero since absolute elements are out of the flow and (unlike floats) will no longer affect how the .window content is laid out (not contribute to the height in this case).
As an aside, instead of using float: left on .wrapper, you can also try overflow: auto which will also establish a new block formatting context but this time the scrolling bar appears on .wrapper instead of .window.
The relevant documentation from w3.org is:
10 Visual formatting model details
10.3 Calculating widths and margins
10.3.5 Floating, non-replaced elements
Link: http://www.w3.org/TR/CSS2/visudet.html#float-width

CSS: relative positioninig of bottom right

The floating element has following structure:
<a>The_button</a>
<div style="position:absolute">
<div style="position:relative" class="inner-box">
Content
Content
Content
Content
Content
</div>
</div>
The content of multiple inner-box controls has variable length, so the inner-box'es have variable height. I want to define CSS class .inner-box (without JavaScript) so that the lower right corner of the inner-box will be positioned in relation to upper-left corner of the link. Is this possible?
Target browsers are IE8+, Firefox, Chrome, Opera, Safari.
Links have always the same height and width.
The only solutions I could come up with so far are:
http://jsfiddle.net/fmVz6/ - this requires a height and width to be defined on the "outer-box", not the inner-box (the inner must be absolutely positioned too).
Working on a second one at the moment ...
http://jsfiddle.net/fmVz6/1/ - this one does not require a height or width specified, it simply needs something inside the parent div (e.g. a space) to see the effect, otherwise the background doesn't appear.
Okay, to have it appear top-left of the link, http://jsfiddle.net/H5G8r/1/ (Requires some rearrangement of your HTML).
This one requires no width to be defined, and doesn't break the words onto multiple lines:
http://jsfiddle.net/H5G8r/2/
Take your pick :-)
You've got the right idea, but backwards. The parent element needs position: relative, and the inner element position: absolute, since the inner element is absolutely positioned relative to its parent (technically, its offsetParent. Specifying position: relative on the parent makes it the offsetParent of all of its child elements).
Next: to align the top-left corner of the parent element with the bottom-right corner of the absolutely positioned child, specify right: 100%; bottom: 100% in the child's CSS. This puts the child <100% of the parent's width> away from the right edge of the parent, and <100% of the parent's height> away from the bottom.
HTML
<div class=outer-box>
The Button
<div class=inner-box>
</div>
</div>​
CSS
.outer-box {
position: relative;
}
.inner-box {
position: absolute;
/* align bottom-right with offsetParent's top-left */
bottom: 100%;
right: 100%;
width: 100px; /* fixed width, else contents will shrink */
}
Also in a jsFiddle: http://jsfiddle.net/ryanartecona/g344W/2/
When you get those aligned, you may want to put another box inside the .inner-box and make it position: relative to make any position adjustments, like sliding it a fixed distance over the button, etc.