386

I find that when I position an element fixed, it doesn't matter if the parent is positioned relative or not, it will position fixed, relative to the window?

#wrapper {
  width: 300px;
  background: orange;
  margin: 0 auto;
  position: relative;
}

#feedback {
  position: fixed;
  right: 0;
  top: 120px;
}
<div id="wrapper">
    ...
    <a id="feedback" href="#">Feedback</a>
</div>

http://jsbin.com/ibesa3

Rifky Niyas
  • 1,737
  • 10
  • 25
Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • 3
    Right answer is here http://stackoverflow.com/questions/4962266/css-positionfixed-inside-a-positioned-element – Soyoes Apr 17 '13 at 10:50
  • 11
    "position:sticky" would be the solution. As of now Apr 2016, Firefox seems to be the only browser that supports this (http://caniuse.com/#feat=css-sticky). – bob Apr 01 '16 at 22:27
  • In recent browsers (released after this question was asked), a better solution is available. See the answer labelled [2016 update](https://stackoverflow.com/a/38796408) – Patrick McElhaney Aug 30 '17 at 15:20

10 Answers10

250

Let me provide answers to both possible questions. Note that your existing title (and original post) ask a question different than what you seek in your edit and subsequent comment.


To position an element "fixed" relative to a parent element, you want position:absolute on the child element, and any position mode other than the default or static on your parent element.

For example:

#parentDiv { position:relative; }
#childDiv { position:absolute; left:50px; top:20px; }

This will position childDiv element 50 pixels left and 20 pixels down relative to parentDiv's position.


To position an element "fixed" relative to the window, you want position:fixed, and can use top:, left:, right:, and bottom: to position as you see fit.

For example:

#yourDiv { position:fixed; bottom:40px; right:40px; }

This will position yourDiv fixed relative to the web browser window, 40 pixels from the bottom edge and 40 pixels from the right edge.

TylerH
  • 20,799
  • 66
  • 75
  • 101
DuckMaestro
  • 15,232
  • 11
  • 67
  • 85
  • 135
    oh, hmm... I actually want to mimic a sticky button, when its positioned absolute, when I scroll down, it doesn't sticky ... hmm – Jiew Meng Mar 06 '11 at 10:49
  • 10
    @Will take that up with the OP who chose it as his answer. Apparently he made edits to his question at some stage later. I'm not in a hurry to delete this answer, since there are still people out there apparently finding it useful (8 upvotes), and is an answer to the title of the question. The OP should pick a different accepted answer if a different one is now the better answer. – DuckMaestro Feb 22 '13 at 19:45
  • 3
    @DuckMaestro what about doing it inside of overflown div with scrollbar? `position:absolute` will make it scroll along with it. – Szymon Toda May 29 '15 at 11:55
  • how can i vertically position fixed and horizontally position absolute of parent. – Muhammad Umer Jul 06 '15 at 22:19
  • 7
    While this solution may in fact have benefited OP, it does not answer the question being asked in the title. The answer below this one is far more accurate and should be marked as the correct one. – David Vasquez Dec 17 '15 at 17:15
  • Like Soyoes comments above on the OP, the correct answer, which is to NOT set left/top/right/bottom on the element itself, can be found here: https://stackoverflow.com/questions/4962266/css-positionfixed-inside-a-positioned-element – lukenofurther Jul 21 '17 at 12:23
  • 1
    You should update your answer since calc helps us resolve this with ease. Assuming that your main container can grow up to 900px for initial values you can set to `right: 0px` after break point hits ```css @media screen and (min-width: 901px) { right: calc((100% - 900px) / 2) } ``` this will again get those elements attached to your main container. – Ertan Kara Dec 08 '19 at 01:09
  • For the sticky button just use ``position: sticky`` – Frizzant May 15 '23 at 08:55
239

The CSS specification requires that position:fixed be anchored to the viewport, not the containing positioned element.

If you must specify your coordinates relative to a parent, you will have to use JavaScript to find the parent's position relative to the viewport first, then set the child (fixed) element's position accordingly.

ALTERNATIVE: Some browsers have sticky CSS support which limits an element to be positioned within both its container and the viewport. Per the commit message:

sticky ... constrains an element to be positioned inside the intersection of its container box, and the viewport.

A stickily positioned element behaves like position:relative (space is reserved for it in-flow), but with an offset that is determined by the sticky position. Changed isInFlowPositioned() to cover relative and sticky.

Depending on your design goals, this behavior may be helpful in some cases. It is currently a working draft, and has decent support, aside from table elements. position: sticky still needs a -webkit prefix in Safari.

See caniuse for up-to-date stats on browser support.

Jon Adams
  • 24,464
  • 18
  • 82
  • 120
  • @rayfranco: It's been a long time though; my guess is the op changed his design to match DuckMaestro's answer (which is a simpler design) and that is why it was accepted? – Jon Adams Aug 21 '12 at 20:00
  • I meant the DuckMaestro's answer is not the right answer to the question. My comment was a bit concise, and is also using a bad wording. Sorry about it, but this answer still should be the accepted one, since it resolve the problem as it is. Just wanted to point it out. Thanks for the explanations on CSS specs. – rayfranco Aug 21 '12 at 20:56
  • 3
    @Jon, you can actually see the edit history if you click the date-time after the post's "edited" note. – JohnK Sep 19 '12 at 22:15
  • 1
    @JohnK I know. By "changed his design" I meant the final code the op used on the site — not a change to the question asked here. – Jon Adams Sep 20 '12 at 01:17
  • 8
    This is not a good solution, `-webkit-sticky` is no longer supported by Chrome (unless you enable the experimental flag). It may come back at some point, but until it's officially supported, best to use Grawl's solution. – Chuck Le Butt Jul 12 '13 at 18:07
  • don't use browser-specific parameters. we have not killed Gecko and Trident yet. – Даниил Пронин Jan 21 '14 at 08:13
  • @Grawl I tried to add _even more_ emphasis regarding the experimental features being experimental and discouraged from use. I didn't want to remove the alternative entirely because in some select (but probably rare) scenarios it still may be useful to someone. – Jon Adams Jan 21 '14 at 18:51
  • 4
    I've just updated this answer now that `sticky` has pretty good support. – jhpratt Jan 05 '18 at 00:01
148

2016 Update

It's now possible in modern browsers to position an element fixed relative to its container. An element that has a transform property acts as the viewport for any of its fixed position child elements.

Or as the CSS Transforms Module puts it:

For elements whose layout is governed by the CSS box model, any value other than none for the transform property also causes the element to establish a containing block for all descendants. Its padding box will be used to layout for all of its absolute-position descendants, fixed-position descendants, and descendant fixed background attachments.

.context {
  width: 300px;
  height: 250px;
  margin: 100px;
  transform: translateZ(0);
}
.viewport {
  width: 100%;
  height: 100%;
  border: 1px solid black;
  overflow: scroll;
}
.centered {
  position: fixed;
  left: 50%;
  bottom: 15px;
  transform: translateX(-50%);
}
<div class="context">
  <div class="viewport">
    <div class="canvas">

      <table>
        <tr>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
        </tr>
        <tr>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
        </tr>

        <tr>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
        </tr>

        <tr>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
          <td>stuff</td>
        </tr>
      </table>

      <button class="centered">OK</button>

    </div>
  </div>
</div>
Patrick McElhaney
  • 57,901
  • 40
  • 134
  • 167
  • 5
    Doesn't work in IE11 – T J Nov 02 '16 at 11:10
  • 2
    Why would you need to do this when a child element with `absolute` positioning acts the same within a parent with `relative` or `absolute` positioning? – Sean Kendle Dec 22 '16 at 14:12
  • 33
    @Sean It's when the parent element has a scroll bar and you want the the fixed position element to stay while the parent element scrolls behind it. – Patrick McElhaney Dec 23 '16 at 20:26
  • 4
    I always just put another div around it, and position absolutely to the outer div, then let the inner div have the scroll bar. There are times when you don't have the control over the HTML, though, so good to know this works! – Sean Kendle Dec 27 '16 at 14:18
  • This is really helpful but it failed when we have scrollbar in parent div and using psedo element in child..can you please look at [my issue](http://stackoverflow.com/questions/43046672/css-pseudo-element-triangle-outside-the-tr-position-misaligned-when-scrollbar) – xkeshav Apr 05 '17 at 10:29
  • 4
    There are a few different CSS properties that have this effect. Another would be `will-change: transform`. – Adam Leggett Jun 27 '17 at 14:59
  • 1
    Its 2017, is there a polyfill or something for the darn IE? – eozzy Nov 22 '17 at 00:31
  • 1
    Thanks for pointing out "transform property acts as the viewport for modern browsers". This was so frustrating to figure out the weird behaviour of "fixed". But sadly, "fixed" acts like "absolute" in that case. z-index property are void. – master_dodo May 29 '19 at 19:03
  • Is this an intended feature or is it something that might disappear after it's "corrected" by these browsers? – andrewtweber Jun 17 '19 at 18:33
  • 1
    @andrewtweber Yes, I just added a link to the spec. It's at the "candidate recommendation" stage so it's a pretty safe bet. – Patrick McElhaney Jun 17 '19 at 21:43
  • not sure why is this so upvoted but it does not work. – strix25 Dec 08 '21 at 22:55
  • how to reverse this effect? – Tayyab Ferozi Jan 02 '22 at 18:34
63

first, set position: fixed and left: 50%, and second — now your start is a center and you can set new position with margin.

suspectus
  • 16,548
  • 8
  • 49
  • 57
  • 1
    Be very careful with this approach! If the fixed element isn't in the viewport it won't work as expected. This fiddle demonstrates the issue if the viewport is too short. http://jsbin.com/igolur/4/edit – Bill Criswell Jul 02 '13 at 18:14
  • 1
    Best solution until `sticky` is better supported in more browsers. – Chuck Le Butt Jul 12 '13 at 18:11
  • you can use additional wrapper inside element with `left: 50%` (`.parent`) and set `left: -50%` for its child (`.child`) and child will be in center. tip: use `pointer-events: none` for `.parent` ;) – Даниил Пронин Feb 04 '14 at 23:01
30

This solution works for me! Reference to the original detailed answer: https://stackoverflow.com/a/11833892/5385623

    
    .level1 {
        position: relative;
    }


    .level2 {
        position: absolute;
    }


    .level3 {
        position: fixed;
        /* Do not set top / left! */
    }
    <div class='level1'>
        <div class='level2'>
            <div class='level3'>
                Content
            </div>
        </div>
    </div>

    
ATP
  • 2,939
  • 4
  • 13
  • 34
Eduard Kolosovskyi
  • 1,396
  • 12
  • 18
19

I know this is super old but after not finding the (pure CSS) answer I was looking for I came up with this solution (partially abstracted from medium.com) and thought it might help others looking to do the same thing.

If you combine @DuckMaestro's answers you can position an element fixed relative to a parent (actually grandparent). Use position: absolute; to position an element inside a parent with position: relative; and then position: fixed; on an element inside the absolute positioned element like so:

HTML

<div class="relative">
  <div class="absolute">
    <a class="fixed-feedback">This element will be fixed</a>
  </div>
</div>

CSS

.relative {
  margin: 0 auto;
  position: relative;
  width: 300px;
}

.absolute {
  position: absolute;
  right: 0;
  top: 0;
  width: 50px;
}

.fixed-feedback {
  position: fixed;
  top: 120px;
  width: 50px;
}

EXAMPLE

Like @JonAdams said, the definition of position: fixed requires the element to be positioned relative to the viewport but you can get around the horizontal aspect of that using this solution.

Note: This is different than just setting a right or left value on the fixed element because that would cause it to move horizontally when a window is resized.

Lifehack
  • 1,981
  • 15
  • 12
  • 2
    This doesn't appear to work the way you claim. The element is still positioning `fixed` relative to the viewport, NOT the containing element. Its position left/right isn't being changed, so it stays where it is that regard, but it is not, in the true sense, fixed according to its parent. Look at this example, I added a div above the parent div. The `div.fixed-feedback` remains at 120px from the top of the window. http://codepen.io/anon/pen/LbvOaY – Sean Kendle Dec 22 '16 at 14:20
  • 1
    @SeanKendle, that is correct. If the element was `fixed` relative to the parent in the truest sense of the word that would just be `absolute`. In this case it is `fixed` vertically to the viewport and horizontally to the parent. That is what I was attempting because it is what the question seems to be asking in my estimation. I may be wrong, though. – Lifehack Dec 22 '16 at 18:51
  • I agree except where you say that `absolute` inside the parent translates to essentially `fixed`, because if it were `absolute`, scrolling would cause it to move with the rest of the contents of the parent div, not remain stationary as it would in a truly `fixed` position. – Sean Kendle Dec 22 '16 at 19:45
10

Here is an example of Jon Adams suggestion above in order to fix a div (toolbar) to the right hand side of your page element using jQuery. The idea is to find the distance from the right hand side of the viewport to the right hand side of the page element and to keep the right hand side of the toolbar there!

HTML

<div id="pageElement"></div>
<div id="toolbar"></div>

CSS

#toolbar {
    position: fixed;
}
....

jQuery

function placeOnRightHandEdgeOfElement(toolbar, pageElement) {
    $(toolbar).css("right", $(window).scrollLeft() + $(window).width()
    - $(pageElement).offset().left
    - parseInt($(pageElement).css("borderLeftWidth"),10)
    - $(pageElement).width() + "px");
}
$(document).ready(function() {
    $(window).resize(function() {
        placeOnRightHandEdgeOfElement("#toolbar", "#pageElement");
    });
    $(window).scroll(function () { 
        placeOnRightHandEdgeOfElement("#toolbar", "#pageElement");
    });
    $("#toolbar").resize();
});
Jon Adams
  • 24,464
  • 18
  • 82
  • 120
KXL
  • 345
  • 3
  • 6
  • 16
    @Grawl ..JQuery was born out of dealing with the madness! As CSS matures and browser compliance grow I hope you will be correct. – KXL Jun 25 '14 at 13:58
9

It's an old post but i'll leave here my javascript solution just in case someone need it.


// you only need this function
function sticky( _el ){
  _el.parentElement.addEventListener("scroll", function(){
    _el.style.transform = "translateY("+this.scrollTop+"px)";
  });
}


// how to make it work:
// get the element you want to be sticky
var el = document.querySelector("#blbl > div");
// give the element as argument, done.
sticky(el);
#blbl{
  position:relative;
  height:200px;  
  overflow: auto;
  background: #eee;
}

#blbl > div{
  position:absolute; 
  padding:50px; 
  top:10px; 
  left:10px; 
  background: #f00
}
<div id="blbl" >
    <div><!-- sticky div --></div> 

    <br><br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>

Notes

  1. I used transform: translateY(@px) because it should be lightweight to compute, high-performance-animations

  2. I only tried this function with modern browsers, it won't work for old browsers where vendors are required (and IE of course)

Sabaz
  • 4,794
  • 2
  • 18
  • 26
2

I know this is an older post, but I think a good example of what Jiew Meng was trying to do can already be found on this site. Check out the side menu located here: https://stackoverflow.com/faq#questions. Looking at it without getting into it too deep, I can tell javascript attaches a fixed position once the scrolling hits below the anchor tag and removes the fixed positioning if the scrolling goes above that same anchor tag. Hopefully, that will get someone started in the right direction.

Community
  • 1
  • 1
illinoistim
  • 456
  • 10
  • 27
1

With multiple divs I managed to get a fixed-y and absolute-x divs. In my case I needed a div on left and right sides, aligned to a centered 1180px width div.

    <div class="parentdiv" style="
        background: transparent;
        margin: auto;
        width: 100%;
        max-width: 1220px;
        height: 10px;
        text-align: center;">
        <div style="
            position: fixed;
            width: 100%;
            max-width: 1220px;">
            <div style="
                position: absolute;
                background: black;
                height: 20px;
                width: 20px;
                left: 0;">
            </div>
            <div style="
                width: 20px;
                height: 20px;
                background: blue;
                position: absolute;                                           
                right: 0;">
            </div>
        </div>
    </div>
sajtdavid
  • 11
  • 1