What is the best approach to restricting an absolutely positioned element's position, ideally in pure CSS?
I know that the following isn't possible but I guess what I'm looking for would look something like:
.stickyElement{
bottom-max:auto;
bottom-min:0px;
top-max: auto;
top-min: 100px;
}
That would allow an element to move to a position no less than 100px from the top of it's containing, positioned element.
An example (and my initial) use case for this is a menu that scrolls as part of a page but stops scrolling when it hits the top of a viewport. (Sticky menus?) an example of which can be seen on this page:
http://spektrummedia.com/startups
I fully expect that this is not possible without using some Javascript but I thought I'd put it out there.
position: sticky
There have been discussions in the W3C about this in recent years. One proposal is the addition of a sticky value for the position property.
.content {
position: -webkit-sticky;
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
position: sticky;
top: 10px;
}
This is currently supported in Chrome 23.0.1247.0 and later as an experimental feature. To enable it, enter about:flags for the URL address and press enter. Then search for "experimental WebKit features" and toggle it between enabled and disabled.
On the html5rocks website, there's a working demo.
Strictly speaking, this is an implementation of sticky content, and not a general-purpose way to limit the minimum or maximum position of an element relative to another element. However, sticky content might be the only practical application for the type of the behavior you're describing.
As there is no way to build this for all major browsers without the use of JavasScript I made my own solution with jQuery:
Assign position:relative to your sticky-top-menu. When it reaches the top of the browser window through scrolling the position is changed to positon:fixed.
Also give your sticky-top-menu top:0 to make sure that it sticks to the top of your browser window.
Here you find a working JSFiddle Example.
HTML
<header>I'm the Header</header>
<div class="sticky-top-menu">
<nav>
Page 1
Page 2
</nav>
</div>
<div class="content">
<p>Some content...</p>
</div>
jQuery
$(window).scroll(function () {
var headerTop = $("header").offset().top + $("header").outerHeight();
if ($(window).scrollTop() > headerTop) {
//when the header reaches the top of the window change position to fixed
$(".sticky-top-menu").css("position", "fixed");
} else {
//put position back to relative
$(".sticky-top-menu").css("position", "relative");
}
});
CSS
.sticky-top-menu {
position:relative;
top: 0px;
width: 100%;
}
I know this post is old, and I might be a little late to it, but to anyone still wondering how to do this i would suggest checking out the clamp() method in CSS, you could do something like this:
top: clamp(30px, 10vw, 50px);
Which would set the min top value to 30px, the ideal value to 10vw, and the max value to 50px.
A media query expression that defines the distance between body 0X 0Y and browser-window 0X 0Y would allow elements to be made sticky after page is scrolled
No such expression has otherwise been proposed and is not supported by any browser, to my knowledge, but it would be a useful expression to allow dynamic configuration of sticky elements, such as menu bars that are sticky after page is scrolled past head, without use of JavaScript.
.this element {
position: absolute;
top: 200px;
}
#media (max-scroll: 200px 0) {
.this.element {
position:fixed;
top: 0;
}
}
To my knowledge, there is no way to restrict an element that was positioned using absolute positioning using solely CSS.
Related
With the new Web Vitals incoming I have a problem with my sticky navigations/menus. In fact most pages will have :-(
The problem is that I use an approach like bootstrap affix to make the menu sticky when it would leave the viewport. But every time the menu leaves the viewport (and enters it as well) and the position is set from relative/static/absolute to fixed it increases CLS (Cumulative Layout Shift). I realize that changing the position to fixed will result in a layout shift because the element is removed from the layer and all following elements will be shifted to the top.
BUT: That's why I came up with some solutions and the best I think is that I use a wrapper with a specific height around the menu. So when the position of the menu changes to fixed the wrapper still exists and does not change in position or height, which means that no following elements has to shift. But the CLS is still counting up. And I do not know what to do to make my menus sticky without affecting the CLS which is important. Btw I cannot use position: sticky because there is not enough browser support. Because if my researches are correct position: sticky works without negatively affecting the CLS, my solution does not although the user **does not see any difference at all **....
Here comes some pseudo code to be more visual:
...
<body>
<h1>
Headline
</h1>
<p>
Here is some elements an stuff
</p>
<div class="menu-wrapper">
<div class="menu">
<ul>...........</ul>
</div>
</div>
<p>
More elements and stuff. Nothing shifting because the wrapper always has the same height.
</p>
...
</body>
...
.menu-wrapper {
height: 60px;
width: 100%;
}
.menu {
height: 60px;
width: 100%;
position: static;
}
.menu.affix {
position: fixed;
top: 0;
}
Any ideas? Thank you very much!
This may be fixed in Chrome Canary 90.
Context: CLS change log reports couple of improvements in this regards: https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/cls.md:
Cumulative Layout Shift Changelog
This is a list of changes to Cumulative Layout Shift.
Metric definition improvement: Bug fixes involving changes to transform, effect, clip or position
Metric definition improvement: Consider transform change countering layout shift.
Other fixes from Chrome 89 listed on the same page may apply also.
I have a div showing "Please wait". The markup for the div is
<div id="pleaseWait" class="divOuterPleaseWait" style="left:expression((documentElement.clientWidth-this.offsetWidth)/2);top:expression(documentElement.scrollTop+((documentElement.clientHeight-this.clientHeight)/2 ));">Please Wait...</div>
This is working fine with IE7. In IE7 the div is show at the center of the page. But excepted behariour is not optained in other browsers (ie. IE8,IE9,FireFox,Google Chrome etc). What should i give to get this working in all browsers? Also can I move the inline style to the my CSS?
A good way to center a div is to use fixed positioning, top and left set to 50% and left and top margin to the negative of half of the width/height:
http://jsfiddle.net/fLa4S/
See this SO answer, or this jsfiddle (press the 'confirm' button). The css you showed in your question is browser specific (especially: IE). In javascript you can center an element by determining the 'viewport' dimensions (height/width of the available screen) and position your element relative to those dimension. The links here demonstrate a way to do that.
It doesn't work in "other browsers" because you are using expressions in your CSS which are 1) incredibly bad for a variety of reasons (slow, deprecated, non-standard) and 2) unnecessary.
You can use pure CSS positioning (percentages and negative margins) or a little JavaScript (jQuery makes this very easy) to accomplish the same thing in all browsers.
Another approach:
<div style="text-align:center;width=200px;margin-left:auto;margin-right:auto">Please wait...</div>
CSS expressions are only working in IE. However, it´s generally not a good idea to use them because they are not W3C conform and in addition they can be very slow when you make heavy use of them.
The CSS attribute position: fixed could help you here:
#pleaseWait {
width: 400px;
height: 200px;
position: fixed; /* IE8+ */
left: 50%;
top: 50%;
margin-left: -200px; /* half of width*/
margin-top: -100px; /* half of height*/
}
If you have to still support <=IE7 you have to use JavaScript (but not within the CSS definition!)
Using auto as margins and defining a width and hight should be enough
<div style="width:200px;height:50px;margin:auto;text-align:center">Please wait ...</div>
If you only want to center verticaly, use margin: 0 auto;
PS: if you want to be more XHTML-correct, put your CSS in a CSS-file and use a class or a id to define the css-styling
I've answered this before: How to set the div in center of the page
Here's a link to what I'll be referring to.
I'm having some trouble getting the background image to work the way I'd like it to.
I want the background to auto resize based on the width of the window, which it is already doing correctly. If you make your window smaller you'll see the background shrink with it.
Here's the issue. If you make your window wide (short) then the background will resize and go too high so you can't see the top of the background anymore (since the background is bottom positioned).
I want the background to be top position when you are at the top of the page, and as you scroll down it will slowly move to be bottom positioned. Sort of like the effect of an Android phone's background when you move left and right. Of course, keep in mind that I still want the background to auto-resize when you make the window smaller.
html {
background-color: #70d4e3;
height: 100%;
}
body {
height: 100%;
}
.background {
margin-top: 45px;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
}
.banner {
margin: 0px auto;
width: 991px;
margin-bottom: -9px;
}
.content {
background: url("http://i.imgur.com/daRJl.png") no-repeat scroll center center transparent;
height: 889px;
margin: 0 auto;
width: 869px;
}
.innerContent {
padding: 30px;
}
<img src="http://i.imgur.com/6d5Cm.jpg" alt="" class="background" />
<div class="banner">
<img src="http://i.imgur.com/JptsZ.jpg" alt="" />
</div>
<div class="content">
<div class="innerContent">
testing
</div>
</div>
Maybe some javascript or jquery would be needed to achieve this.
Well, this was fun, thanks!
I hope you don't mind me taking the liberty to use percentages to make my life a little bit easier and possibly the script slightly more robust since I can reliably use floats with percentages.
What I did is make the layout, html and css comply with the rules you need for the bg to be animated properly, they stayed largely the same from what you had.
Then it was just a question of figuring out the calculations needed with the right properties to figure out the percentage you were from the top, the *20 is actually the amount of space 'left' to fill by the background image in percentages (as the background height is 80%).
They I moved the calculations to a function so I could call that on scroll and on window resize, making sure it's initiated on any event that modifies the window somehow...
Didn't do extensive testing but it worked in Chrome and I'm tired :p
I believe this is what you are looking for:
http://jsfiddle.net/sg3s/RSqrw/15/ See edit 2
If you wanted this the other way arround just make the page background start at the top and modify that:
http://jsfiddle.net/sg3s/RSqrw/14/ See edit 2
Edit:
As a bonus, and since I had never actually written jquery script as a 'plugin', I decided to convert this into one. What I came up with should be easy to implement and use!
http://jsfiddle.net/sg3s/RSqrw/52/ See Edit 3
Functionality successfully tested in Chrome, Firefox 3.6, IE9 + compatibility mode
Edit 2:
Reading the question again checking if I did it right I noticed I didn't quite do what you want, so I updated the link in the first edit which gives you a plugin in which you can have several options for the scrolling background. It retains my 'old' interpetation while also doing what you want... Read comments in code for some extra descriptions.
Edit 3:
As I went to work today I was bothered with the fact that my plugin 'try' was a little bloated. And as you mentioned in the comment it didn't quite fit the requirements.
So I rewrote it to only do what you want and not much more, tested in Chrome Firefox, IE9 +compat etc etc.. This script is a lot cleaner.
http://jsfiddle.net/sg3s/vZxHW/
You can chose to make the background stick to the top or bottom if the height fits in the window. Nothing else, but that is already more than enough to do some pretty cool stuff :p
An exact solution: Fiddle: http://jsfiddle.net/srGHE/2/show/
View source
Thanks for the challenge. See below for the solution, which is complying with all requirements, including recommended yet optional (with steps on how to remove these) features. I only show the changed parts of your page, with an explanation after each section (CSS, HTML and JavaScript):
CSS (changes):
html,body{
margin: 0;
height: 100%;
padding: 0;
}
body{
background-color: #70d4e3;
}
#background { /*Previously: .background*/
/*Removed: margin-top: 45px;
No other changes*/
}
#banner /*Previously: .banner; no other changes */
#content /*Previously: .content; no other changes */
#innerContent /*Previously: .innerContent; no other changes */
Explanation of CSS revisions:
margin-top:45px at the background is unnecessary, since you're absolutely positioning the element.
All of the elements which are unlikely to appear more than once should be selected via the id (#) selector. This selector is more specific than the class selector.
HTML (changes):
All of the class attributes have been replaced by id. No other changes have been made. Don't forget to include the JQuery framework, because I've implemented your wishes using JQuery.
JavaScript (new):
Note: I have added a feature which you didn't request, but seems logical. The code will automatically reserve sufficient margin at the left side of the window in order to always display the background. Remove anything between the marked comments if you don't want this feature.
$(document).ready(function(){
//"Static" variables
var background = $("#background");
var marginTop = parseFloat(background.css("margin-top")) || 0;
var bannerWidth = $("#banner").width(); /*Part of auto left-margin */
var extraContWidth = (bannerWidth - $("#content").width())/2; /*Same as above*/
function fixBG(){
var bodyWidth = $("body").width();
var body_bg_width_ratio = bodyWidth/1920;
var bgHeight = body_bg_width_ratio * 926; //Calcs the visible height of BG
var height = $(document).height();
var docHeight = $(window).height();
var difHeight = bgHeight - docHeight;
var scrollDif = $(document).scrollTop() / (height - docHeight) || 0;
/*Start of automatic left-margin*/
var arrowWidth = body_bg_width_ratio * 115; //Arrow width
if(bodyWidth - bannerWidth > arrowWidth*2){
$("body > div").css("margin-left", "auto");
} else {
$("body > #banner").css("margin-left", arrowWidth+"px");
$("body > #content").css("margin-left", (arrowWidth+extraContWidth)+"px");
}
/*End of automatic left-margin*/
if(difHeight > 0){
background.css({top:(-scrollDif*difHeight-marginTop)+"px", bottom:""});
} else {
background.css({top:"", bottom:"0"});
}
}
$(window).resize(fixBG);
$(window).scroll(fixBG);
fixBG();
});
Explanation of the JavaScript code
The size of the background is determined by calculating the ratio of the background and document width. The width property is used, because it's the most reliable method for the calculation.
Then, the height of the viewport, document body and background is calculated. If applicable, the scrolling offset is also calculated, to prepare the movement of the background, if necessary.
Optionally, the code determines whether it's necessary to adjust the left margin (to keep the background visible at a narrow window).
Finally, if the background arrow has a greater height than the document's body, the background is moved accordingly, taking the scrolling position into account. The arrow starts at the top of the document, and will move up as the user scrolls (so that the bottom side of the arrow will be the bottom of the page when the user has fully scrolled down). If it's unnecessary to move the background, because it already suits well, the background will be positioned at the bottom of the page.
When the page has finished loading, this functionality is added to the Resize and scroll events, so that the background is always at the right location.
If you've got any other questions, feel free to ask them.
well, I'm not sure if I understand you and why do you want to do that, but you can try adding 2 backgrounds (see http://www.css3.info/preview/multiple-backgrounds/ ), one with the top bg and another with the bottom bg but I think that if the page is not too long it will cause issues, so the other answer with pure CSS is as follows: first add 3 horizontal divs with 100% width. Top div will have your top bg and its height, middle div will be transparent and auto height and bottom div will have your bottom bg and its height. All divs will have a 0 z-index. Then create a higher z-index div to act as a container and you'll be set. If I understand your question right, that's the close I can think of to achieve that. This being said, I'm pretty sure you can do this with JQuery with way better results
Using jQuery I was able to give you what I think you're asking for:
$(window).scroll(function() {
var h = Math.max($(document).height(), $(window).height());
var bottom = h - $(".background").height() - $(window).height();
$(".background").css("top", (($(window).scrollTop() / h) * bottom) + "px");
});
EDIT: Forgot to account for the way scrollTop reports position.
Or maybe:
.background {
margin-top: 45px;
max-width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
max-height: 100%;
}
I reccomend using jQuery Background Parallax
http://www.stevefenton.co.uk/Content/Jquery-Background-Parallax/
The function is as simple as
$("body").backgroundparallax();
Ask if you don't get it to work.
#abney; as i understand your question may that's you want http://jsfiddle.net/sandeep/RSqrw/60/
you need only css for this:
#background {
position: fixed;
width: 100%;
height:100%;
top: 0;
left:0;
z-index: -1;
}
The solution to your issue is a nice little lightweight plugin by Scott Robin. You can get more info, download it, and make your life easier for all of your projects by visiting his project page here.
I was wondering if anybody knows how to keep a image/control always visible when the user scrolls up and down the page.
An example is on the new Google search page that automatically perdicts your results when you type in. On the right-hand side there is a map that starts roughtly 100px from the top of the page. When you scroll down the map remains in view but is 0px from top of page. When you scroll back up, it returns to its normal position, 100px from the top of the page.
Any ideas on how this is done?
You are looking for position: fixed
<div style="position: fixed; right: 100px; top: 100px; width: 200px; height: 100px; background-color: red">
I'm fixed</div>
Works in all browsers except IE6.
position:fixed is the way to go here. If you want to achieve the described effect like on the google page, you'll need a little javascript that toggles the element's css-styles depending on the scroll position.
You can find a script for that task here:
http://jsfiddle.net/6bnuU/
Note that depends on jquery. If you're looking for a solution that does not require jquery, you can easily rewrite the script, but you'll have to use a browser switch because IE uses a different API for the scrolling state than the other browsers.
Also note that IE 6 does not support position:fixed, so if you want to support it you'll have to emulate it with javascript (which is an easy task)
Use CSS position with the value fixed, then position it on the page with left, right, top and/or bottom. For example:
#some_element {
position: fixed;
top: 100px;
right: 0;
}
The exact example you mention also requires some JavaScript.
How can I create a DIV block that always stays at the bottom of my page? When scrolling more content should show up right above the block. The only solution i can think of is to use 2 iframes but I prefer using CSS.
Update: The solution needs to work on iOS
Here's some CSS:
.bottomFixed {
position:fixed;
bottom: 0;
/* technically not necessary, but helps to see */
background-color: yellow;
padding: 10px;
}
Here's some HTML:
<div class="bottomFixed">Hello, world!</div>
This div would be placed at the bottom of the screen and stay there. Note: this won't work on iOS because of the way it does scrolling.
div.bottom {
position:fixed;
}
Then just move it where you want. Unfortunately, browser support is limited. IE6 for example doesn't support this option for position. Also note that this removes the div from the flow, so you'll have to make sure there's enough space for the viewer to see stuff at the bottom of the page with the div on top.