Advertisement
Advertisement


Fixed position but relative to container


Question

I am trying to fix a div so it always sticks to the top of the screen, using:

position: fixed;
top: 0px;
right: 0px;

However, the div is inside a centered container. When I use position:fixed it fixes the div relative to the browser window, such as it's up against the right side of the browser. Instead, it should be fixed relative to the container.

I know that position:absolute can be used to fix an element relative to the div, but when you scroll down the page the element vanishes and doesn't stick to the top as with position:fixed.

Is there a hack or workaround to achieve this?

2015/09/27
1
655
9/27/2015 11:31:39 AM

Accepted Answer

Short answer: no. (It is now possible with CSS transform. See the edit below)

Long answer: The problem with using "fixed" positioning is that it takes the element out of flow. thus it can't be re-positioned relative to its parent because it's as if it didn't have one. If, however, the container is of a fixed, known width, you can use something like:

#fixedContainer {
  position: fixed;
  width: 600px;
  height: 200px;
  left: 50%;
  top: 0%;
  margin-left: -300px; /*half the width*/
}

http://jsfiddle.net/HFjU6/1/

Edit (03/2015):

This is outdated information. It is now possible to center content of an dynamic size (horizontally and vertically) with the help of the magic of CSS3 transform. The same principle applies, but instead of using margin to offset your container, you can use translateX(-50%). This doesn't work with the above margin trick because you don't know how much to offset it unless the width is fixed and you can't use relative values (like 50%) because it will be relative to the parent and not the element it's applied to. transform behaves differently. Its values are relative to the element they are applied to. Thus, 50% for transform means half the width of the element, while 50% for margin is half of the parent's width. This is an IE9+ solution

Using similar code to the above example, I recreated the same scenario using completely dynamic width and height:

.fixedContainer {
    background-color:#ddd;
    position: fixed;
    padding: 2em;
    left: 50%;
    top: 0%;
    transform: translateX(-50%);
}

If you want it to be centered, you can do that too:

.fixedContainer {
    background-color:#ddd;
    position: fixed;
    padding: 2em;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

Demos:

jsFiddle: Centered horizontally only
jsFiddle: Centered both horizontally and vertically
Original credit goes to user aaronk6 for pointing it out to me in this answer

2020/06/20
417
6/20/2020 9:12:55 AM

Actually this is possible and the accepted answer only deals with centralising, which is straightforward enough. Also you really don't need to use JavaScript.

This will let you deal with any scenario:

Set everything up as you would if you want to position: absolute inside a position: relative container, and then create a new fixed position div inside the div with position: absolute, but do not set its top and left properties. It will then be fixed wherever you want it, relative to the container.

For example:

/* Main site body */
.wrapper {
    width: 940px;
    margin: 0 auto;
    position: relative; /* Ensure absolute positioned child elements are relative to this*/
}

/* Absolute positioned wrapper for the element you want to fix position */
.fixed-wrapper {
    width: 220px;
    position: absolute;
    top: 0;
    left: -240px; /* Move this out to the left of the site body, leaving a 20px gutter */
}

/* The element you want to fix the position of */
.fixed {
    width: 220px;
    position: fixed;
    /* Do not set top / left! */
}
<div class="wrapper">
    <div class="fixed-wrapper">
        <div class="fixed">
            Content in here will be fixed position, but 240px to the left of the site body.
        </div>
    </div>
</div>

Sadly, I was hoping this thread might solve my issue with Android's WebKit rendering box-shadow blur pixels as margins on fixed position elements, but it seems it's a bug.
Anyway, I hope this helps!

2017/10/12

Yes, according to the specs, there is a way.

While I agree that Graeme Blackwood's should be the accepted answer, because it practically solves the issue, it should be noted that a fixed element can be positioned relatively to its container.

I noticed by accident that when applying

-webkit-transform: translateZ(0);

to the body, it made a fixed child relative to it (instead of the viewport). So my fixed elements left and top properties were now relative to the container.

So I did some research, and found that the issue was already been covered by Eric Meyer and even if it felt like a "trick", turns out that this is part of the specifications:

For elements whose layout is governed by the CSS box model, any value other than none for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.

http://www.w3.org/TR/css3-transforms/

So, if you apply any transformation to a parent element, it will become the containing block.

But...

The problem is that the implementation seems buggy/creative, because the elements also stop behaving as fixed (even if this bit doesn't seem to be part of specification).

The same behavior will be found in Safari, Chrome and Firefox, but not in IE11 (where the fixed element will still remain fixed).

Another interesting (undocumented) thing is that when a fixed element is contained inside a transformed element, while its top and left properties will now be related to the container, respecting the box-sizing property, its scrolling context will extend over the border of the element, as if box-sizing was set to border-box. For some creative out there, this could possibly become a plaything :)

TEST

2014/09/29

The answer is yes, as long as you don't set left: 0 or right: 0 after you set the div position to fixed.

http://jsfiddle.net/T2PL5/85/

Checkout the sidebar div. It is fixed, but related to the parent, not to the window view point.

body {
  background: #ccc;
}

.wrapper {
  margin: 0 auto;
  height: 1400px;
  width: 650px;
  background: green;
}

.sidebar {
  background-color: #ddd;
  float: left;
  width: 300px;
  height: 100px;
  position: fixed;
}

.main {
  float: right;
  background-color: yellow;
  width: 300px;
  height: 1400px;
}
<div class="wrapper">wrapper
  <div class="sidebar">sidebar</div>
  <div class="main">main</div>
</div>

2020/08/24

position: sticky that is a new way to position elements that is conceptually similar to position: fixed. The difference is that an element with position: sticky behaves like position: relative within its parent, until a given offset threshold is met in the viewport.

In Chrome 56 (currently beta as of December 2016, stable in Jan 2017) position: sticky is now back.

https://developers.google.com/web/updates/2016/12/position-sticky

More details are in Stick your landings! position: sticky lands in WebKit.

2018/10/10

2019 SOLUTION: You can use position: sticky property.

Here is an example CODEPEN demonstrating the usage and also how it differs from position: fixed.

How it behaves is explained below:

  1. An element with sticky position is positioned based on the user's scroll position. It basically acts like position: relative until an element is scrolled beyond a specific offset, in which case it turns into position: fixed. When it is scrolled back it gets back to its previous (relative) position.

  2. It effects the flow of other elements in the page ie occupies a specific space on the page(just like position: relative).

  3. If it is defined inside some container, it is positioned with respect to that container. If the container has some overflow(scroll), depending on the scroll offset it turns into position:fixed.

So if you want to achieve the fixed functionality but inside a container, use sticky.

2019/08/19

Source: https://stackoverflow.com/questions/6794000
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]