Search code examples
csspositioncss-positionfixed

position fixed div in absolute positioned div works - but why?


I need to give an element position fixed, but I can't position it relatively to the viewport, I need it to be positioned relatively to a container.

I managed to do so, but I wonder how and why it works, because actually I think position fixed is ALWAYS positioned relatively to the viewport and NOT to parent elements.

Here my (working) code:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">

body {
    height: 2000px;
}

.container {
    position: relative;
}

.sidebar {
    width: 200px;
    background-color: red;
    height: 200px;
    position: absolute;
    left: 100px;
    top: 100px;
}

.fixed {
    width: 100px;
    height: 100px;
    background-color: green;
    position: fixed;
    /* top: 0;
    left: 0; */
    margin-left: 10px;
    margin-top: 10px;
}

</style>
</head>

<body>

<div class="container">
    <div class="sidebar">
        <div class="fixed"></div>
    </div>
</div>

</body>
</html>

Fiddle: https://jsfiddle.net/tnwLycao/

Element "fixed" can easily be positioned with margins (e.g. margin-left/margin-top). However, when I deactivate the margins and try to position "fixed" with top/left it positions itself relatively to the viewport again, not relatively to the parent container/element.

Can someone give me a hint how and why this works?


Solution

  • An element with position: fixed is indeed positioned relative to the viewport (or browser). However, because it is an absolutely positioned element, it is "positioned relative to the initial containing block established by the viewport".

    This is laid out in the position documentation:

    An absolutely positioned element is an element whose computed position value is absolute or fixed. The top, right, bottom, and left properties specify offsets from the edges of the element's containing block. (The containing block is the ancestor relative to which the element is positioned.) If the element has margins, they are added to the offset.

    That is to say, when you specify margin-top and margin-left, these values are relative to the parent. And because the element is positioned relative to the parent, the default top and left are inherited from the parent. In your example, the .fixed element has a top value of 100px because it inherits the top value of 100px from .sidebar (the parent). When you set top: 0 on .fixed, you are overriding this value (going from top: 108px to top: 0):

    Fixed

    Because of this, the element appears to be taken 'out of flow'. However, it is still always positioned relative to the viewport; it just had an initial offset (which it inherited from its parent).