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?
CSS
#wrapper { width: 300px; background: orange; margin: 0 auto; position: relative; }
#feedback { position: fixed; right: 0; top: 120px; }
HTML
<div id="wrapper">
...
<a id="feedback" href="#">Feedback</a>
</div>
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.
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!
<div id="pageElement"></div>
<div id="toolbar"></div>
#toolbar {
position: fixed;
}
....
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();
});
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.
This solution works for me! Reference to the original detailed answer: https://stackoverflow.com/a/11833892/5385623
css:
.level1 {
position: relative;
}
.level2 {
position: absolute;
}
.level3 {
position: fixed;
/* Do not set top / left! */
}
html:
<div class='level1'>
<div class='level2'>
<div class='level3'>
Content
</div>
</div>
</div>
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 {_x000D_
width: 300px;_x000D_
height: 250px;_x000D_
margin: 100px;_x000D_
transform: translateZ(0);_x000D_
}_x000D_
.viewport {_x000D_
width: 100%;_x000D_
height: 100%;_x000D_
border: 1px solid black;_x000D_
overflow: scroll;_x000D_
}_x000D_
.centered {_x000D_
position: fixed;_x000D_
left: 50%;_x000D_
bottom: 15px;_x000D_
transform: translateX(-50%);_x000D_
}
_x000D_
<div class="context">_x000D_
<div class="viewport">_x000D_
<div class="canvas">_x000D_
_x000D_
<table>_x000D_
<tr>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
</tr>_x000D_
<tr>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
</tr>_x000D_
_x000D_
<tr>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
</tr>_x000D_
_x000D_
<tr>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
<td>stuff</td>_x000D_
</tr>_x000D_
</table>_x000D_
_x000D_
<button class="centered">OK</button>_x000D_
_x000D_
</div>_x000D_
</div>_x000D_
</div>
_x000D_
It's an old post but i'll leave here my javascript solution just in case someone need it.
// you only need this function_x000D_
function sticky( _el ){_x000D_
_el.parentElement.addEventListener("scroll", function(){_x000D_
_el.style.transform = "translateY("+this.scrollTop+"px)";_x000D_
});_x000D_
}_x000D_
_x000D_
_x000D_
// how to make it work:_x000D_
// get the element you want to be sticky_x000D_
var el = document.querySelector("#blbl > div");_x000D_
// give the element as argument, done._x000D_
sticky(el);
_x000D_
#blbl{_x000D_
position:relative;_x000D_
height:200px; _x000D_
overflow: auto;_x000D_
background: #eee;_x000D_
}_x000D_
_x000D_
#blbl > div{_x000D_
position:absolute; _x000D_
padding:50px; _x000D_
top:10px; _x000D_
left:10px; _x000D_
background: #f00_x000D_
}
_x000D_
<div id="blbl" >_x000D_
<div><!-- sticky div --></div> _x000D_
_x000D_
<br><br><br><br><br><br><br><br><br><br><br><br><br>_x000D_
<br><br><br><br><br><br><br><br><br><br><br><br><br>_x000D_
<br><br><br><br><br><br><br><br><br><br><br><br><br>_x000D_
<br><br><br><br><br><br><br><br><br><br><br><br><br>_x000D_
</div>
_x000D_
Notes
I used transform: translateY(@px) because it should be lightweight to compute, high-performance-animations
I only tried this function with modern browsers, it won't work for old browsers where vendors are required (and IE of course)
first, set position: fixed
and left: 50%
, and second — now your start is a center and you can set new position with margin.
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>
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:
<div class="relative">
<div class="absolute">
<a class="fixed-feedback">This element will be fixed</a>
</div>
</div>
.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;
}
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.
Source: Stackoverflow.com