Position element fixed vertically, absolute horizontally - css

Here's what I'm trying to accomplish:
I need a button which is positioned a certain distance from the right side of a div, and is always that same distance from the side of the div no matter the size of the viewport, but will scroll with the window.
So it is x pixels from the right side of the div at all times, but y pixels from the top of the view port at all times.
Is this possible?

Position Fixed Element Horizontally Based Off Another Element
(Disclaimer Note: The solution offered here is not technically "absolute horizontally" as the question title stated, but did achieve what the OP originally wanted, the fixed position element to be 'X' distance from the right edge of another but fixed in its vertical scroll.)
I love these types of css challenges. As a proof of concept, yes, you can get what you desire. You may have to tweak some things for your needs, but here is some sample html and css that passed FireFox, IE8 and IE7 (IE6, of course, does not have position: fixed).
HTML:
<body>
<div class="inflow">
<div class="positioner"><!-- may not be needed: see notes below-->
<div class="fixed"></div>
</div>
</div>
</body>
CSS (borders and all dimensions for demonstration):
div.inflow {
width: 200px;
height: 1000px;
border: 1px solid blue;
float: right;
position: relative;
margin-right: 100px;
}
div.positioner {position: absolute; right: 0;} /*may not be needed: see below*/
div.fixed {
width: 80px;
border: 1px solid red;
height: 100px;
position: fixed;
top: 60px;
margin-left: 15px;
}
The key is to not set the left or right at all for the horizontal on the div.fixed but use the two wrapper divs to set the horizontal position. The div.positioner is not needed if the div.inflow is a fixed width, as the left margin of the div.fixed can be set to known width of the container. However, if you desire than container to have a percentage width, then you will need the div.positioner to push the div.fixed to the right side of the div.inflow first.
Edit: As an interesting side note, when I set overflow: hidden (should one need to do that) on the div.inflow had no effect on the fixed position div being outside the other's boundaries. Apparently the fixed position is enough to take it out of the containing div's context for overflow.

After much digging (including this post) I couldn't find a solution that I liked. The Accepted Answer here doesn't do what the OP's title read, and the best solutions I could find admittedly resulted in jumpy elements. Then, it hit me: Have the element be "fixed", detect when a horizontal scroll occurs, and switch it to be absolutely positioned. Here is the resulting code:
View it as a Code Pen.
HTML
<div class="blue">
<div class="red">
</div>
</div>
CSS
/* Styling */
.blue, .red {
border-style: dashed;
border-width: 2px;
padding: 2px;
margin-bottom: 2px;
}
/* This will be out "vertical-fixed" element */
.red {
border-color: red;
height: 120px;
position: fixed;
width: 500px;
}
/* Container so we can see when we scroll */
.blue {
border-color: blue;
width: 50%;
display: inline-block;
height: 800px;
}
JavaScript
$(function () {
var $document = $(document),
left = 0,
scrollTimer = 0;
// Detect horizontal scroll start and stop.
$document.on("scroll", function () {
var docLeft = $document.scrollLeft();
if(left !== docLeft) {
var self = this, args = arguments;
if(!scrollTimer) {
// We've not yet (re)started the timer: It's the beginning of scrolling.
startHScroll.apply(self, args);
}
window.clearTimeout(scrollTimer);
scrollTimer = window.setTimeout(function () {
scrollTimer = 0;
// Our timer was never stopped: We've finished scrolling.
stopHScroll.apply(self, args);
}, 100);
left = docLeft;
}
});
// Horizontal scroll started - Make div's absolutely positioned.
function startHScroll () {
console.log("Scroll Start");
$(".red")
// Clear out any left-positioning set by stopHScroll.
.css("left","")
.each(function () {
var $this = $(this),
pos = $this.offset();
// Preserve our current vertical position...
$this.css("top", pos.top)
})
// ...before making it absolutely positioned.
.css("position", "absolute");
}
// Horizontal scroll stopped - Make div's float again.
function stopHScroll () {
var leftScroll = $(window).scrollLeft();
console.log("Scroll Stop");
$(".red")
// Clear out any top-positioning set by startHScroll.
.css("top","")
.each(function () {
var $this = $(this),
pos = $this.position();
// Preserve our current horizontal position, munus the scroll position...
$this.css("left", pos.left-leftScroll);
})
// ...before making it fixed positioned.
.css("position", "fixed");
}
});

I arrived here looking for a solution to a similar problem, which was to have a footer bar that spans the width of the window and sits below the (variable height and width) content. In other words, make it appear that the footer is "fixed" with respect to its horizontal position, but retains its normal postion in the document flow with respect to its vertical position. In my case, I had the footer text right-aligned, so it worked for me to dynamically adjust the width of the footer. Here is what I came up with:
HTML
<div id="footer-outer">
<div id="footer">
Footer text.
</div><!-- end footer -->
</div><!-- end footer-outer -->
CSS
#footer-outer
{
width: 100%;
}
#footer
{
text-align: right;
background-color: blue;
/* various style attributes, not important for the example */
}
CoffeeScript / JavaScript
(Using prototype.js).
class Footer
document.observe 'dom:loaded', ->
Footer.width = $('footer-outer').getDimensions().width
Event.observe window, 'scroll', ->
x = document.viewport.getScrollOffsets().left
$('footer-outer').setStyle( {'width': (Footer.width + x) + "px"} )
which compiles into:
Footer = (function() {
function Footer() {}
return Footer;
})();
document.observe('dom:loaded', function() {
return Footer.width = $('footer-outer').getDimensions().width;
});
Event.observe(window, 'scroll', function() {
var x;
x = document.viewport.getScrollOffsets().left;
return $('footer-outer').setStyle({
'width': (Footer.width + x) + "px"
});
});
This works nicely in FireFox, and pretty well in Chrome (it's a little jittery); I haven't tried other browsers.
I also wanted any spare space below the footer to be a different colour, so I added this footer-stretch div:
HTML
...
</div><!-- end footer -->
<div id="footer-stretch"></div>
</div><!-- end footer-outer -->
CSS
#footer-outer
{
height: 100%;
}
#footer-stretch
{
height: 100%;
background-color: #2A2A2A;
}
Note that for the #footer-stretch div to work, all the parent elements up to the body element (and possibly the html element - not sure) must have a fixed height (in this case, height = 100%).

Related

Issue with positioning and sticky scroll of absolute div

I have two divs side by side, the first on about 60% of the page is positioned as "relative" on the left, the second is placed as "absolute" on the right as it is the only way I managed to place them side by side.
The div on the right is only about 10% (measures about 1 view port height) of the full height of the webpage. The div on the left which measures roughly 10 viewport heights defines the full height of the webpage. Hence, I would like to be able to have the right div slide down as the user scrolls down so as to not leave a blank space on the right of the left div below the right div.
The issue is that I can't manage to have the right div set as sticky and scroll down and still keep them right next to eachother at the top when the page first loads. The sticky div will be on top whhile the left div starts just when the sticky div finishes. Basically it behaves the same as if I set both of them relative but I need the right divv to behave as an absolute div before it becomes sticky to preserve the positioning.
With absolute positioning:
.mainbodyfx {
width: 60vw;
padding-left: 10vw;
right: 40vw;
margin-left: 0;
margin-right: 0;
height: 10vh;
}
.floatingfxbuy {
position: absolute;
background-color: transparent;
width: 20vw;
left: 75%;
height:1vh;
}
<div> Content of full height and width slider </div>
<div class=floatingfxbuy> Right div that needs to slide down with scroll </div>
<div class="mainbodyfx"> Left div that defines the height of the whole webpage</div>
With sticky positioning:
.mainbodyfx {
width: 60vw;
padding-left: 10vw;
right: 40vw;
margin-left: 0;
margin-right: 0;
height: 10vh;
}
.floatingfxbuy {
position: sticky;
background-color: transparent;
width: 20vw;
left: 75%;
height:1vh;
}
<div> Content of full height and width slider </div>
<div class=floatingfxbuy> Right div that needs to slide down with scroll </div>
<div class="mainbodyfx"> Left div that defines the height of the whole webpage</div>
So, it's hard to tell exactly what you're asking for but I think I'm close to what you're asking for. Essentially if you want a floating side div you need to treat it as completely separate from the other div. Really as far as the css and html goes the .floatingfxbuy div is separate from the entire page.
If you want the floating div to be absolute positioned until you scroll to a certain height you need to use JavaScript to change the position to fixed for the div when the window scrolls to a certain point.
You also need to have the z-index slightly higher on the floating div so that it doesn't interact with any elements "underneath" it.
Here is a quick example I threw together. Sorry about the terrible colors.
$(document).ready(function() { // at document ready run this function
var $window = $(window); // local variable to window
$window.on('scroll resize', function() { // on window scroll or resize run this function
if ($window.scrollTop() > 50) { // if the top of the window is lower than 50px then add the fix class to the .floating-side-div
$('.floating-side-div').addClass('fix');
} else { // if the top of the window is heigher than 100px remove the fix class
$('.floating-side-div').removeClass('fix');
}
});
});
body {
margin: 0;
/* get rid of some default body styles */
}
.page-container {
min-height: 200vh;
/* set height of page so we can scroll to test */
width: 100%;
background-color: green;
}
.content-div {
width: 60vw;
/* width you suggested */
height: 50vh;
/* random height for content */
margin-left: 10vw;
/* some left margin you want */
background-color: red;
}
.floating-side-div {
height: 10vh;
/* 10% viewport height like you want */
width: 20vw;
/* width you have in your css */
background-color: blue;
position: absolute;
/* to start we want absolute position */
right: 0;
/* put it at the right of the page */
top: 0;
/* put it all the way at the top. you can change this if you want */
z-index: 99;
/* increase z-index so we're over top of the other elements on the page and don't distort the page when scrolling */
}
.floating-side-div.fix {
position: fixed;
/* change from absolute to fix so we 'fix' the div to a spot in the viewport. in this example top: 0, right: 0; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="page-container">
<!-- our page container -->
<div class="content-div"></div>
<!-- the content div(your .mainbodyfx) -->
<div class="floating-side-div"></div>
<!-- the floating div(your .floatingfxbuy) -->
</div>

How to scroll fixed area when footer is reached?

Is it possible to achive the following configuration in CSS please ?
I have a long page with a footer in the bottom (the footer display property is flex)
In the visible area of the page I need the fixed area to be always in the bottom as long as the scrolling has not reached the footer.
Once the scrolling has reached the footer, the fixed area should scroll up to be on the top of the footer like in the following screenshots:
The fixed area is in the bottom of the visible region of the page.
When scrolling, if the footer is not reached, the fixed are will remain at the bottom of the page
When reaching the footer, the fixed position is on the top of the footer
I tried something like:
FixedArea {
position: fixed;
bottom: 0;
width: 100%;
}
but when I scroll until the footer the fixed region disappears.
You can nest the body content together with the fixed content in a element that has a height: 100vh on it, and overflow: auto on the actual content of that page, in that way the content will scroll independently of the fix element, and once reached the end the body scroll will continue on till the end of page (footer)
I created an example. Try: https://jsfiddle.net/pvviana/wwc8LgLm/
I am changing the div css property "position" at the bottom of page.
Code:
<div class="foo">Hello</div>
<footer>OKAY</footer>
Javascript(Jquery):
var $logo = $('.foo');
$(document).scroll(function() {
$logo.css({position: $(this).scrollTop()>100 ? "relative":"fixed"});
});
Css :
.foo {
position: fixed;
bottom: 0px;
}
Here is a (another) possible jQuery solution.
During scroll, calculate the distance remaining until the bottom of the window, and start setting the bottom style property on your fixed area the height of the footer minus the distance remaining, otherwise make sure it's set (back) to the original, as follows (note, I set the height of the content block to 800px, so make sure you try this so that the result window has a smaller height than that):
var originalBottom = 0; // get this depending on your circumstances
var footerHeight = 72; // get this depending on your circumstances
$(window).scroll(function () { // start to scroll
// calculating the distance from bottom
var distanceToBottom = $(document).height() - $(window).height() - $(window).scrollTop();
if (distanceToBottom <= footerHeight) // when reaching the footer
$("#fixed-area").css('bottom', (footerHeight - distanceToBottom) + 'px');
else // when distancing from the footer
$("#fixed-area").css('bottom', originalBottom + 'px'); // only need to specify 'px' (or other unit) if the number is not 0
});
body {
padding: 0;
margin: 0;
}
#content {
height: 800px;
}
#fixed-area {
position: fixed;
bottom: 0;
padding: 5px;
margin: 5px;
border: 1px solid green;
width: calc(100% - 22px); /* padding (2*5) + border (2*1) + margin (2*5) */
text-align: center;
}
#footer {
height: 40px;
position: relative;
bottom: 0;
padding-top: 20px;
margin: 5px;
border: 1px solid black;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="fixed-area">Fixed Area</div>
<div id="content"></div>
<div id="footer">Footer</div>

Floating Div Crosshairs - Confine To Div

I'm trying to add a crosshair-style cursor effect to a div that contains a D3 Datamap. I've got it working using jQuery but the crosshairs seem to overlap their parent div on the bottom and right, but not the top and left.
I've created this fiddle to demonstrate.
I've tried changing the position property of the crosshair div to no avail.
On my page, changing position to absolute seems to correctly confine the crosshairs to the container, but the center point is offset from the cursor (e.pageX, e.pageY). However, I cannot recreate this in the fiddle, as fixed, static, relative, absolute make no difference.
One solution I've found is to set the width and height properties of the container and hairs to fixed values. However, I need the container to be responsive.
First, the vertical and horizontal lines also overlapping the box on the top and left, it was not visible because of the body-viewport ;).
Second, I did some investigation and found out that the best solution would be to place the crosshair-lines inside the map-container which are positioned absolute to the map-container. Therefore we have better control of the position and behavior of the crosshair-lines and the map-container can be flexible aswell!
I added the default cursor for better testing. — https://jsfiddle.net/9r4rtcz9/6/ – code snippet below
//Map Hover Crosshairs
$(function() {
var cH = $('#crosshair-h'),
cV = $('#crosshair-v');
$('.map_wrapper').on('mouseover', function() {
cH.css('visibility', 'visible');
cV.css('visibility', 'visible');
$('.map_wrapper').bind('mousemove', function(e) {
cH.css('top', e.pageY);
cV.css('left', e.pageX);
});
});
$('.map_wrapper').on('mouseout', function() {
cH.css('visibility', 'hidden');
cV.css('visibility', 'hidden');
$('.map_wrapper').unbind("mousemove");
});
});
.map_wrapper {
position: relative;
height: 100px;
width: 300px;
border: 1px solid black;
overflow: hidden;
cursor: default;
}
.hair {
float: left;
position: absolute;
background-color: rgba(100, 100, 100, 0.5);
z-index: 10;
pointer-events: none;
}
#crosshair-h {
width: 100%;
height: 2px;
margin-top: -8px;
visibility: visible;
}
#crosshair-v {
height: 100%;
width: 2px;
margin-left: -8px;
visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="map_wrapper">
<div class="hair" id="crosshair-h"></div>
<div class="hair" id="crosshair-v"></div>
</div>
<p class="datamaps">D3 Data Maps Here</p>

"Fixed" positioned before pseudo element

How can I make the icon::before pseudo element not scroll. I want it to have a "fixed" position; not relative to the window, but to div.scrollable.icon
Here's the demo I'm talking about: http://codepen.io/anon/pen/VLWdEm
===UPDATE===
There's a problem here. Here's the new codepen: http://codepen.io/kiranm/pen/QbgxZV
How do I make icon::before "fixed" relative to div.scrollable.icon?
Ok, I understood your problem. Since we all tried the code with the preview expanded we didn't see that when we shrink the preview the div was fixed relative to the window.
So I came up with this, although I couldn't do it just with CSS, I had to add a little of jQuery and modify your HTML structure.
HTML
<div class="scrollable">
<div class="icon"></div>
text
</div>
CSS
.scrollable {
border: 1px solid tomato;
width: 200px;
height: 200px;
overflow: auto;
position: absolute;
}
.icon {
position: relative;
}
.icon::before {
content: 'An icon';
background-color: yellow;
padding: 2em;
}
And the jQuery
var $ = jQuery || $;
$(document).ready(function() {
$('.scrollable').on('scroll', function() {
var top = $('.scrollable').scrollTop() + $('.scrollable').offset().top;
$('.icon').css({'top': top + 'px'});
});
});
As you can see I added another CSS rule so I can manipulate it with jQuery, and with jQuery I took the value of the scroll in the container div and I add to it its value to the top of the window, and I assign that result to the top of the icon so it will be "fixed" to the div. Also I moved the icon div to be a child of scrollable so I can manipulate it separately.
Here's the pen http://codepen.io/anon/pen/QbgxzV
I hope it helps you.
Try like this: Demo
.icon::before {
content: 'An icon';
background-color: yellow;
padding: 2em;
position: fixed; / * changed position absolute to fixed */
}
I cant understand your requirement?
Position fixed is working for relative to window screen only. otherwise you set icon position:relative then give icon::before position absolute.

Is it possible to keep the width of the parent element when position: fixed is applied?

When we apply position:fixed to an element, it's taken out of the normal flow of the document, therefore it doesn't respect it's parent's element width.
Are there ways to make it inherit it's parent's width if this is declared as a percentage ? (working use case below)
let widthis = $('.box').width();
$('.dimensions').text(`width is ${widthis}`);
$('button').on('click', function() {
$('.box').toggleClass('fixed');
let widthis = $('.box').width();
$('.dimensions').text(`width is ${widthis}`);
});
.container {
max-width: 500px;
height: 1000px;
}
.box {
background-color: lightgreen;
}
.fixed {
position: fixed;
}
.col-1 {
border: 1px solid red;
float: left;
width: 29%;
}
.col-2 {
border: 1px solid pink;
float: left;
width: 69%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click this to toggle position fixed for the left column</button>
<div class="container">
<div class="col-1">
<div class="box">
fixed content<br>
<span class="dimensions"></span>
</div>
</div>
<div class="col-2">
some other content
</div>
</div>
This is an interesting challenge. To approach this, we should first understand what fixed actually does.
Understand Fixed
Unlike absolute, fixed doesn't position itself from its closest relative parent. Instead, fixed positions itself relative to the viewport. The viewport will always stay fixed, which is why you get the effect that you do.
That being said, whenever you "inherit" any width it will be respective to the viewport. So it does us no good when we're trying set the width of our target element to the width of it's parent.
Learn more about the different behaviors of position.
Quick Solutions
There are two approaches to fix this.
Pure CSS
We can use pure CSS to fix this problem, but we would need to know the width in advance. Suppose that its parent element is 300px;
.parent{
width: 300px;
}
.parent .fixed_child{
position: fixed;
width: 300px;
}
JS
Now with mobile devices, we don't really have the luxury of having set widths, especially anything over 300px. Using percentages won't work either, since it will be relative to the viewport and not the parent element. We can use JS, in this case with jQuery to achieve this. Lets take a look at a function that will always set the width of the parent at the given moment:
function toggleFixed () {
var parentwidth = $(".parent").width();
$(".child").toggleClass("fixed").width(parentwidth);
}
css:
.fixed{
position:fixed;
}
View in CodePen
Dynamic Widths
That's fine and dandy, but what happens if the width of the window changes while the user is still on the page, changing the parent element with this? While the parent may adjust its width, the child will stay the set width that the function set it. We can fix this with jQuery's resize() event listener. First we'll need to split the function we created into two:
function toggleFixed() {
adjustWidth();
$(".child").toggleClass("fixed");
}
function adjustWidth() {
var parentwidth = $(".parent").width();
$(".child").width(parentwidth);
}
Now that we've separated each part, we can call them individually, we'll include our original button method that toggles the fixed and width:
$("#fixer").click(
function() {
toggleFixed();
});
And now we also add the resize event listener to the window:
$(window).resize(
function() {
adjustWidth();
})
View in CodePen
There! Now we have a fixed element who's size will be adjusted when the window is resized.
Conclusion
We've tackled this challenge by understanding fixed position and it's limitations. Unlike Absolute, fixed only relates to the view port and therefore cannot inherit its parent's width.
To solve this, we need to use some JS magic, which didn't take very much with jQuery, to achieve this.
In some cases, we need a dynamic approach with scaling devices of varying widths. Again, we took the JS approach.
You can use width:inherit. This will make it listen to parent. I test it and it works in Firefox.
The width is changing because the object when static is receiving its percentage width from its parent. Once you set the object to fixed it is no longer in flow and resizes.
You're gonna have to set a size to your nav menu on its own and not expect the element to get its width from the parent.
.nav {
position: fixed;
width: 20%;
border: 1px solid green;
padding: 0px;
list-style-type:none;
background:lightblue;
}
http://tinker.io/3458e/5
Hi you could also use jquery to keep the width. For example:
jQuery(function($) {
function fixDiv() {
var $cache = $('#your-element');
var $width = $('#your-element').parent().width();
if ($(window).scrollTop() > 100) {
$cache.css({
'position': 'fixed',
'top': '10px',
'width': $width
});
} else {
$cache.css({
'position': 'relative',
'top': 'auto'
});
}
}
$(window).scroll(fixDiv);
fixDiv();
});
As someone already suggest, using plain javascript (without jquery):
const parentElement = document.querySelector('.parent-element');
const fixedElement = document.querySelector('.fixed-element');
window.addEventListener('load', changeFixedElementWidth);
window.addEventListener('resize', changeFixedElementWidth);
function changeFixedElementWidth() {
const parentElementWidth = parentElement.getBoundingClientRect().width;
fixedElement.style.width = parentElementWidth + 'px';
}
This is likely because of some default margin or padding on the <html> or <body> element. When it's static, it sizes based on the <body>'s width at the time, but when it changes to position:fixed it's sized in respect to the viewport.
As such, removing that default margin/padding should fix the problem (in my experience body { margin:0; } fixes it) as should changing the sizing when it is fixed (like width:calc(n% - 5px);).
A workaround might be: left:8px; right:0; width:18%; in the CSS for the nav.
Not the best solution though.
In my case, I wanted a responsive UI, but I also want a fixed sidebar. What I did was find the maximum width on the largest screen in pixels (250px in my case). Then I used:
<script>
.parent {
maxWidth: '1500px';
margin: 'auto auto';
}
.left {
flex: 1;
}
.center {
flex: 4;
}
.right {
flex: 1;
}
.fixedTop {
position: 'fixed';
maxWidth: '250px';
}
</script>
<div className="parent">
<div className="left">left side</div>
<div className="center">center</div>
<div className="right">
<div className="fixedTop">
<label>I am responsively sized horizontally up to my max width.</label>
</div>
</div>
</div>
This does not allow you to inherit width as requested, but it may help avoid that need if you are trying to do what I did.
I was here on this thread at first, but my problem was not solved, so I did a work around myself amnd it worked in my case. here it is..
`
.parent{
position: "sticky";
top, right, bottom, left: // adjust distance from sides so that it will be like in fixed position
}
.child{
width:"100%"
}
`
Add width:auto; to the element with position:fixed; to make its width equal to the width of its parent element.

Resources