Fixed position but relative to container

1,199,656

Solution 1

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

Solution 2

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!

Solution 3

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

Solution 4

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>

Solution 5

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.

Share:
1,199,656
Zach Nicodemous
Author by

Zach Nicodemous

Updated on July 22, 2022

Comments

  • Zach Nicodemous
    Zach Nicodemous almost 2 years

    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?

  • Joseph Marikle
    Joseph Marikle almost 13 years
    one problem I can see with this approach is that it relies on a timer (2.5 seconds) and during that time, if the user resizes the window, it will take up to that time for it to correct itself. That's not the best thing when it comes to user experience. Also, if it can be helped, try using event listeners instead of timers.
  • webLacky3rdClass
    webLacky3rdClass almost 13 years
    Like I said it a OLD script, one of my first, and have not needed to go back a fix it. So it is not the best , but it can be a good starting point.
  • Iain M Norman
    Iain M Norman over 12 years
    Worked beautifully for me. Although I needed mine to the left of and already centered, unfixed div, so just had to decrease the left margin until it was aligned. No stays aligned no matter what the width of the windows. Thanks. +1
  • hobberwickey
    hobberwickey about 12 years
    Why the minus? This works perfectly for fixing an element wherever you want in the flow of the DOM. deadlykitten.com/tests/fixedPositionTest.php
  • Eric K
    Eric K about 12 years
    My ad was 140px wide, so I moved it 150px to the left to give it a little bit of right margin against the edge of the website frame.
  • Flash
    Flash over 11 years
    @Joseph any idea why position:fixed without specifying top or left sometimes works? Some browsers appear to default the element to where it would normally be, if it had normal positioning. stackoverflow.com/questions/8712047/…
  • JasonWyatt
    JasonWyatt over 11 years
    I don't think this is accomplishing quite what the OP asked about.
  • mfq
    mfq over 11 years
    what about without fixed height and width ? In my case I haven't given any signal width to any container even up to body tag. What solution you will say me to do.
  • Joseph Marikle
    Joseph Marikle over 11 years
    @mfq Unfortunately, this method won't work at all without a known height and width. What you go with will largely depend on what you are trying to center. If it's an image, just use it as a background of a 100% x 100% container. If it's an actual element that is completely dynamic, you will probably have to explore using a javascript solution to get the dimensions.
  • o_O
    o_O almost 11 years
    Not quite ANY scenario but it's a good start. If there's a lot of space below .wrapper then your .fixed-wrapper will end at the bottom of the parent but your .fixed element will continue to flow out of both containers and into any content below. Like I said, it's a good start, not perfect, but I don't see how this can be accomplished without some js.
  • vvohra87
    vvohra87 almost 11 years
    Problem is that the div in question flickers when scrolling.
  • Mr Br
    Mr Br almost 11 years
    Probably because scrolling is refreshing too much or too fast. I didn't have that problem tbh, but I can suggest you try with using global "blocker" which would be refreshed for example every 10ms or some time, and set to blocking value every time you make some height change. Of course you have to add if condition to .scroll event that is checking if in this period (10ms) you already scrolled div. I repeat I haven't tried but I believe it could work :) On this way you could stop div from changing it's position too much so that computer can't actually follow, and enough so human eye see's it.
  • vvohra87
    vvohra87 almost 11 years
    I already gave your answer +1 mate ;) The pain with attaching a global looping method is really performance. To get something like that to work nicely in older pc's and tablets is painful. Many a time a junior dev at work has written a 'var x' inside such a method resulting in needless memory leaks if the page is left open too long :P But yea your suggestion seems like the only real option.
  • Mr Br
    Mr Br almost 11 years
    You got point with global loops; isn't good, I agree. I just like to think about this kind of problems, that's why I wrote another suggestion :) Tbh, now looking it little better, I would avoid it xD I got another solution now. What you say about setting setTimeout() on scroll event which calls func. that makes div movement..also you use global blocker which is set to true while waiting for timeout and when timeout is called you return blocker to false which enables another call after some time...on this way you wouldn't use global loop but still have control on frequency of div movement.
  • vvohra87
    vvohra87 almost 11 years
    It's more elegant but I find it hard to accept. This is the sort of thing I expect the html and css specs to handle tbh. Hate the fact that js is involved in the first place!
  • Francesco Frapporti
    Francesco Frapporti over 10 years
    @JosephMarikle I guess you should give a try to my answer below, and perhaps update the "short answer", because this little hack is working perfectly fine for webkit, and many times it's all one needs.
  • Joseph Marikle
    Joseph Marikle over 10 years
    @FrancescoFrapporti That's a nice CSS hack but I think I'll keep my answer as is for a couple of reasons. For one, transform is not w3c standard (draft only) and thus subject to change. With behavior being undefined and this being (as far as I can see) a side effect of the intended style, this has potential to break at some point. The second reason is that this is a webkit only solution. Thank you for adding your answer though. plurality of solutions is what makes these kind of questions so useful to future developers trying to find a solution to their current bugs and development issues.
  • Zach Saucier
    Zach Saucier over 10 years
  • Zuhaib Ali
    Zuhaib Ali over 10 years
    This doesn't play well with fluid design, however.
  • Mirage
    Mirage about 10 years
    i tried to do this, but as and when i scroll down, the header which i had applied fixed(with parent absolute to which parent was relative) didn't move as i scroll.
  • Anish Patel
    Anish Patel about 10 years
    Nice, this works for me in Chrome. Any ideas on how to achieve the same in IE? I've tried using translateZ but it didn't in any version of IE.
  • Tallboy
    Tallboy almost 10 years
    Same.. does anyone know how to do this in IE? its kind of a pain to detect browser, then systematically show or not show left: (considering that messes it up)
  • html_programmer
    html_programmer over 9 years
    Any reason why this doesn't get more upvotes? Seems the most obvious solution to me.
  • mytharcher
    mytharcher over 9 years
    I just meet the opposite condition which I need fixed in relative container but changed by transform property. Your answer is very helpful, thank you!
  • Damien
    Damien over 9 years
    This worked for me with a top-right div in Chromium and Firefox, except that the vertical positions of the fixed div were different (on Chromium it was on top of the parent div, on Firefox it was inside).
  • Willster
    Willster over 9 years
    I have found that adding left:inherit to the fixed position element can force the browser to respect the parent's offset.
  • Joseph Marikle
    Joseph Marikle over 9 years
    @Willster fascinating! I'd very much like to see a working example. Do you have fiddle set up demonstrating that method?
  • jjenzz
    jjenzz about 9 years
    @Francesco Frapporti seems this no longer works... in the latest version of Chrome anyway (Version 42.0.2311.90) :(
  • dmi3y
    dmi3y about 9 years
    Solid cross-browser solution, works in IE8 like a charm.
  • Alexandre Bourlier
    Alexandre Bourlier almost 9 years
    What about cross browser compatibility. Did any of you checked this?
  • Philip Stratford
    Philip Stratford almost 9 years
    By accident, I wrapped the <div> with position:absolute inside the <div> with position:fixed instead, and that works perfectly well, even when resizing the window, etc., at least in Chrome and IE. So it's: <div style="position:relative"> <div style="position:fixed"> <div style="position:absolute;top:0px;"></div> </div> </div>
  • Rhys
    Rhys about 8 years
    This is correct but only for the 'positioning' of the fixed element. It will not respect the container width or resize in any way. For example, If you have a fluid wrapper with max-width:1000px; margin:auto; this will not work. If everything can be a fixed width, then yes this will solve your problem.
  • Jason Frank
    Jason Frank almost 8 years
    @AlexandreBourlier Yes, this worked well for me for a fixed element in Chrome, Firefox, and IE11 at least.
  • Ananda Prasad Bandaru
    Ananda Prasad Bandaru over 7 years
    Worked perfectly. Had to add margin-left:X% to bring the element to the right of the container
  • Kyle Vassella
    Kyle Vassella over 6 years
    I still have the problem of my width collapsing when I give an element a position:fixed;. Without giving a hard width of 1140px; (which works, but if screen is resized, width is too long), how can I set the original width (1140px) back to my fixed element and still retain responsive width?
  • Kyle Vassella
    Kyle Vassella over 6 years
    But by setting all these hard widths, don't we lose the responsiveness? Is there a way to implement a responsive-width fixed div? Using percentages hasn't worked for me, since width:100% means 100% of the viewport width, not the width of my bootstrap container that my fixed element is a child of.
  • timebandit
    timebandit almost 6 years
    How does this work as according to w3c An element with position: fixed; is positioned relative to the viewport? Please enlighten me.
  • Shawn
    Shawn almost 6 years
    Don't understand what is the point of position:fixed here. Only position: absolute is needed to achieve the same.
  • Shawn
    Shawn almost 6 years
    @timebandit it is still relative to the viewport. It's just that without specifying top and left, it's positioned based on parent's position by default.
  • ricks
    ricks over 5 years
    This makes life so much easier, the only issue is that sticky is not supported in Internet Explorer or Edge 15 and earlier versions.
  • Yarin
    Yarin over 5 years
    position:sticky is great. Supported by all browsers except IE caniuse.com/#feat=css-sticky
  • fat_mike
    fat_mike about 5 years
    Totally wrong. Sidebar is positioned to the window view point and not the parent view point. Try this with a smaller wrapper in height. jsfiddle.net/xf0jygcz
  • Vladislav  Gritsenko
    Vladislav Gritsenko about 5 years
    lol, so old api, and you save my day... im' using it for fixed columns for flex tables.
  • Fikret
    Fikret almost 5 years
    "within its parent" - is it possible to stay sticky element at top, either if parent scrolled up became invisible?
  • dave0688
    dave0688 almost 5 years
    Nice solution. I didn't know about position: sticky yet. Helped a lot, thanks!
  • TerrorBight
    TerrorBight about 4 years
    I would add to this - if your not sure whether left/top are being set use style="left:unset;right:unset;top:unset;bottom:unset;"
  • dkellner
    dkellner about 4 years
    I was just about to post this. Good answer! sticky is the way.
  • Johann
    Johann almost 4 years
    If you increase the output window size in Codepen, the fixed element is not fixed but moves up. Not good. A better solution keeps it anchored to the bottom of the viewport.
  • Pransh Tiwari
    Pransh Tiwari almost 4 years
    @AndroidDev Only the top element is defined to be fixed for the entire page. It is being rendered as desired. The other elements are explanatory examples for different cases.
  • tklives
    tklives almost 4 years
    This actually worked for me too! Because what I am working with is anchored to the far right side of the page-wrapper, I just added a transform: translateX(-300px); (width of the element I needed fixed) in order to get it where I needed. I'm truly surprised this works, but happy to have found it!
  • thiout_p
    thiout_p almost 4 years
    Perfect fit, thank you! For anyone wondering: works for Chrome 84.0.4147.89 and Firefox 78.0.2 and Edge 44.18362.449.0
  • nullspace
    nullspace over 3 years
    You can also achieve this with applying will-change
  • G-Cyrillus
    G-Cyrillus over 3 years
    FF 81 let header scroll up and dessapear, but sticky do work fine.
  • AndreVitorio
    AndreVitorio about 3 years
    It's important to keep in mind that position: sticky won't work if any of the parent elements uses the overflow: hidden property.
  • Uzumaki Naruto
    Uzumaki Naruto over 2 years
    Thankyou so much man. Really appreciate it. <3
  • corwin.amber
    corwin.amber over 2 years
    sticky is much better because that also allows to set width: 100%, relative to the containing div, which the previous solutions (fixed with no left or right) do not allow.
  • arsdever
    arsdever over 2 years
    The provided might not have been working 10 years ago. But it's nice to share what works at least now.
  • Shubham Jha
    Shubham Jha over 2 years
    @arsdever Yes, right.
  • Alex
    Alex over 2 years
    what kind of hackery is that?? it totally works! You can use it with click passthrough when you want the absolute div to be invisible (stackoverflow.com/questions/3680429/…)
  • isherwood
    isherwood over 2 years
    @NikitasIO, your comment is vague and can become obsolete. Better to comment on other answers to explain why they're not valid.
  • mufazmi
    mufazmi over 2 years
    Wow. It's one of the best answer. Really appreciate it. <3
  • strix25
    strix25 almost 2 years
    not sure why is this accepted answer, but it does not work!
  • strix25
    strix25 almost 2 years
    this does not work at all
  • strix25
    strix25 almost 2 years
    position: sticky is one of the most useless things ever one parent has overflow:hidden and it stops working