开发者

Fixed position but relative to container

开发者 https://www.devze.com 2023-03-22 12:49 出处:网络
I am trying to fix a div so it always sticks to the top of the screen, using: position: fixed; top: 0px;

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?


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


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.


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


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>


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.


2019 SOLUTION: You can use position: sticky property.

Here is an example CODEPEN demonstrating the usage and also how it differs from position: fixed.

How it behaves is explained below:

  1. An element with sticky position is positioned based on the user's scroll position. It basically acts like position: relative until an element is scrolled beyond a specific offset, in which case it turns into position: fixed. When it is scrolled back it gets back to its previous (relative) position.

  2. It effects the flow of other elements in the page ie occupies a specific space on the page(just like position: relative).

  3. If it is defined inside some container, it is positioned with respect to that container. If the container has some overflow(scroll), depending on the scroll offset it turns into position:fixed.

So if you want to achieve the fixed functionality but inside a container, use sticky.


It is possible to position an element with fixed position relative to its container if the container is using certain containment rules.

<div class='parent'>
  <div class='child'></div>
</div>
.parent {
  contain: content;
}

.child {
  position: fixed;
  top: 0;
  left: 0;
}


Just take the top and left styles out of the fixed position div. Here's an example

<div id='body' style='height:200%; position: absolute; width: 100%; '>
    <div id='parent' style='display: block; margin: 0px auto; width: 200px;'>
        <div id='content' style='position: fixed;'>content</div>
    </div>
</div> 

The #content div will be sit wherever the parent div sits, but will be fixed there.


I have created this jQuery plugin to solve a similar issue I was having where I had a centered container (tabular data), and I wanted the header to fix to the top of the page when the list was scrolled, yet I wanted it anchored to the tabular data so it would be wherever I put the container (centered, left, right) and also allow it to move left and right with the page when scrolled horizontally.

Here is the link to this jQuery plugin that may solve this problem:

https://github.com/bigspotteddog/ScrollToFixed

The description of this plugin is as follows:

This plugin is used to fix elements to the top of the page, if the element would have scrolled out of view, vertically; however, it does allow the element to continue to move left or right with the horizontal scroll.

Given an option marginTop, the element will stop moving vertically upward once the vertical scroll has reached the target position; but, the element will still move horizontally as the page is scrolled left or right. Once the page has been scrolled back down passed the target position, the element will be restored to its original position on the page.

This plugin has been tested in Firefox 3/4, Google Chrome 10/11, Safari 5, and Internet Explorer 8/9.

Usage for your particular case:

<script src="scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery-scrolltofixed-min.js" type="text/javascript"></script>

$(document).ready(function() {
    $('#mydiv').scrollToFixed();
});


I had to do this with an advertisement that my client wanted to sit outside of the content area. I simply did the following and it worked like a charm!

<div id="content" style="position:relative; width:750px; margin:0 auto;">
  <div id="leftOutsideAd" style="position:absolute; top:0; left:-150px;">
    <a href="#" style="position:fixed;"><img src="###" /></a>
  </div>
</div>


You have a solution if you change position: fixed; to position: sticky;

So your code should be:

position: sticky;
top: 0;
right: 0;

now other divs will not slip underneath.


Two HTML elements and pure CSS (modern browsers)

See this jsFiddle example. Resize and see how the fixed elements even move with the floated elements they are in. Use the inner-most scroll bar to see how the scroll would work on a site (fixed elements staying fixed).

As many here have stated, one key is not setting any positional settings on the fixed element (no top, right, bottom, or left values).

Rather, we put all the fixed elements (note how the last box has four of them) first in the box they are to be positioned off of, like so:

<div class="reference">
  <div class="fixed">Test</div>
  Some other content in.
</div>

Then we use margin-top and margin-left to "move" them in relation to their container, something like as this CSS does:

.fixed {
    position: fixed;
    margin-top: 200px; /* Push/pull it up/down */
    margin-left: 200px; /* Push/pull it right/left */
}

Note that because fixed elements ignore all other layout elements, the final container in our fiddle can have multiple fixed elements, and still have all those elements related to the top left corner. But this is only true if they are all placed first in the container, as this comparison fiddle shows that if dispersed within the container content, positioning becomes unreliable.

Whether the wrapper is static, relative, or absolute in positioning, it does not matter.


Disclaimer:

This answers just the title: Fixed position but relative to container. For the actual use case the user is asking for position: sticky is the way to go.


https://developer.mozilla.org/en-US/docs/Web/CSS/position

It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform, perspective, or filter property set to something other than none

You just need to add a transform to the container and the position of the fixed element will be relative to the container. I think a transform: translateX(0); should be enough.


You can give a try to my jQuery plugin, FixTo.

Usage:

$('#mydiv').fixTo('#centeredContainer');


With pure CSS you can't manage to do it; at least I haven't. However you can do it with jQuery very simply. I'll explain my problem, and with a little change you can use it.

So for a start, I wanted my element to have a fixed top (from top of the window), and a left component to inherit from the parent element (because the parent element is centered). To set the left component, just put your element into the parent and set position:relative for the parent element.

Then you need to know how much from the top your element is when the a scroll bar is on top (y zero scrolled); there are two options again. First is that is static (some number) or you have to read it from the parent element.

In my case it's 150 pixels from top static. So when you see 150 it's how much is the element from the top when we haven't scrolled.

CSS

#parent-element{position:relative;}
#promo{position:absolute;}

jQuery

$(document).ready(function() { //This check window scroll bar location on start
    wtop=parseInt($(window).scrollTop());
    $("#promo").css('top',150+wtop+'px');

});
$(window).scroll(function () { //This is when the window is scrolling
    wtop=parseInt($(window).scrollTop());
    $("#promo").css('top',150+wtop+'px');
});


Yes it is possible as long as you don't set the position (top or left, etc.) of your fixed element (you can still use margin to set a relative position, though). Simon Bos posted about this a while ago.

However don't expect fixed element to scroll with non-fixed relatives.

See a demo here.


I have the same issue, one of our team members gives me a solution. To allowed the div fix position and relative to other div, our solution is to use a parent container wrap the fix div and scroll div.

.container {
  position: relative;
  flex:1;
  display:flex;
}

.fix {
  position:absolute;
}
<div class="container">
  <div class="scroll"></div>
  <div class="fix"></div>
</div>


Another weird solution to achieve a relative fixed position is converting your container into an iframe, that way your fixed element can be fixed to it's container's viewport and not the entire page.


This is easy (as per HTML below)

The trick is to NOT use top or left on the element (div) with "position: fixed;". If these are not specified, the "fixed content" element will appear RELATIVE to the enclosing element (the div with "position:relative;") INSTEAD OF relative to the browser window!!!

<div id="divTermsOfUse" style="width:870px; z-index: 20; overflow:auto;">
    <div id="divCloser" style="position:relative; left: 852px;">
        <div style="position:fixed; z-index:22;">
            <a href="javascript:hideDiv('divTermsOfUse');">
                <span style="font-size:18pt; font-weight:bold;">X</span>
            </a>
        </div>
    </div>
    <div>  <!-- container for... -->
         lots of Text To Be Scrolled vertically...
         bhah! blah! blah!
    </div>
</div>

Above allowed me to locate a closing "X" button at the top of a lot of text in a div with vertical scrolling. The "X" sits in place (does not move with scrolled text and yet it does move left or right with the enclosing div container when the user resizes the width of the browser window! Thus it is "fixed" vertically, but positioned relative to the enclosing element horizontally!

Before I got this working the "X" scrolled up and out of sight when I scrolled the text content down.

Apologies for not providing my javascript hideDiv() function, but it would needlessly make this post longer. I opted to keep it as short as possible.


/* html */

/* this div exists purely for the purpose of positioning the fixed div it contains */
<div class="fix-my-fixed-div-to-its-parent-not-the-body">

     <div class="im-fixed-within-my-container-div-zone">
          my fixed content
     </div>

</div>



/* css */

/* wraps fixed div to get desired fixed outcome */
.fix-my-fixed-div-to-its-parent-not-the-body 
{
    float: right;
}

.im-fixed-within-my-container-div-zone
{
    position: fixed;
    transform: translate(-100%);
}


It's possible if you use JavaScript. In this case, the jQuery plugin Sticky-Kit:


I created a jsfiddle to demostrate how this works using transform.

HTML

<div class="left">
    Content
</div>
<div class="right">
<div class="fixedContainer">
    X
</div>
    Side bar
</div>

CSS

body {
  margin: 0;
}
.left {
  width: 77%;
  background: teal;
  height: 2000px;
}
.right {
  width: 23%;
  background: yellow;
  height: 100vh;
  position: fixed;
  right: 0;
  top: 0;
}
.fixedContainer {
    background-color:#ddd;
    position: fixed;
    padding: 2em;
    //right: 0;
    top: 0%;
    transform: translateX(-100px);
}

jQuery

$('.fixedContainer').on('click', function() {
    $('.right').animate({'width': '0px'});
  $('.left').animate({'width': '100%'});
});

https://jsfiddle.net/bx6ktwnn/1/


My project is .NET ASP Core 2 MVC Angular 4 template with Bootstrap 4. Adding "sticky-top" into main app component html (i.e. app.component.html) on the first row worked, as follows:

<div class='row sticky-top'>
    <div class='col-sm-12'>
        <nav-menu-top></nav-menu-top>
    </div>
</div>
<div class="row">
    <div class='col-sm-3'>
        <nav-menu></nav-menu>
    </div>
    <div class='col-sm-9 body-content'>
        <router-outlet></router-outlet>
    </div>
</div>

Is that the convention or did I oversimplify this?


When you use position:fixed CSS rule and try to apply top/left/right/bottom it position the element relative to window.

A workaround is to use margin properties instead of top/left/right/bottom


The magic is to take the screen width minus the container width and divide it by 2:

//1400px is according to container max-width (left can be also right)
.fixed {
  position: fixed;
  right: calc((100vw - 1400px)/2);
}

Note: css calc() function is almost, but not 100% supported. For most use-cases it is definitely supported enough. Click here for more details

Snippet (with a 300px container just to fit this website's widget):

.container {
  max-width: 300px;
  margin-left: auto;
  margin-right: auto;
}


.fixed {
  position: fixed;
  right: calc((100vw - 300px)/2);
}


@media screen and (max-width: 300px) {
  right: 0px;
}
<div style="height: 3000px">
    <div class="container">
        <button class="fixed">
            FIXED CONTENT
        </button>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Laborum, eum? Animi quidem accusamus minima vel, dolorem suscipit quia accusantium minus harum modi commodi distinctio! Iste voluptatum earum quam voluptate culpa ad, ipsum dolorum recusandae quis atque eligendi necessitatibus magnam nisi dolores beatae qui? Perspiciatis natus cum nostrum in quod odio sapiente doloremque rerum quo dolore tenetur facere, quisquam atque accusamus fugiat eligendi, deleniti nisi minus recusandae distinctio dignissimos! Dicta quos ipsum qui pariatur at vel veritatis veniam quisquam minus modi et voluptas aliquam laborum, cumque in quo magnam sapiente. Expedita ut dolore laboriosam tempora reprehenderit vero eaque blanditiis, cumque voluptatibus, velit nemo, veniam tenetur quisquam numquam adipisci quae odio repellendus neque incidunt! Cum odio corporis soluta voluptate nesciunt, quasi nobis deleniti neque porro expedita fugiat placeat alias autem pariatur animi error, dicta veritatis atque perspiciatis inventore tempora dolor ad! Mollitia in dolorem ipsam eos porro magni perspiciatis possimus maiores, itaque facere ut. Eos culpa eum error quia incidunt repellendus quam possimus, asperiores earum ipsum molestias dicta sint fugit atque veniam dolorum illo? Officia harum sint incidunt totam, reiciendis illum eos maxime sequi neque repellat quis, expedita eum, corporis quaerat nemo qui soluta aspernatur animi. Sint ad rem numquam omnis sit.
    </div>
 </div>


To achieve a similar purpose as the one requested "fixed position relative to container", for me position:fixed was not working since when I scrolled the page the element remained in a fixed position, but you can achieve a similar effect with the overflow feature.

On the snipet below, check how the heading appears fixed when in the parent container, but when you scroll the body it scrolls too

#grandpa{
    width: 100vw;
    height: 120vh;
    background-color:rgb(236, 233, 233);
    overflow:hidden;
}
#parent{
    width: 200px;
    height: 200px;
    background-color:teal;
    overflow:hidden;
}
#child1{
    padding:1em
}
#child2{
    height: 100px;
    background-color:khaki;
    overflow-y:scroll;
}
<div id="grandpa">
    <div id="parent">
        <div id="child1">
            heading
        </div>
        <div id="child2">
            <p>body</p>
            <p>body</p>
            <p>body</p>
            <p>body</p>
            <p>body</p>
            <p>body</p>
            <p>body</p>
            <p>body</p>
        </div>
    </div>
</div>


I did something like that awhile back. I was pretty new to JavaScript, so I'm sure you can do better, but here is a starting point:

function fixxedtext() {
    if (navigator.appName.indexOf("Microsoft") != -1) {
        if (document.body.offsetWidth > 960) {
            var width = document.body.offsetWidth - 960;
            width = width / 2;
            document.getElementById("side").style.marginRight = width + "px";
        }
        if (document.body.offsetWidth < 960) {
            var width = 960 - document.body.offsetWidth;
            document.getElementById("side").style.marginRight = "-" + width + "px";
        }
    }
    else {
        if (window.innerWidth > 960) {
            var width = window.innerWidth - 960;
            width = width / 2;
            document.getElementById("side").style.marginRight = width + "px";
        }
        if (window.innerWidth < 960) {
            var width = 960 - window.innerWidth;
            document.getElementById("side").style.marginRight = "-" + width + "px";
        }
    }
    window.setTimeout("fixxedtext()", 2500)
}

You will need to set your width, and then it gets the window width and changes the margin every few seconds. I know it is heavy, but it works.


I am late to answer this question but I am sure this is a common problem to exist in future as well and will definitely help the future searchers.

This can be solved by applying a trick. We can use calc here. Within calc we can use 100 vertical width and subtract 90% of the width and divide by 2. Please make changes to the 90% as per your needs. In your case it can be 100% or 70% whatever depending upon your specific requirement.

.your-class {
  position: fixed;
  right: calc((100vw - 90%)/2);
}

This worked for me. Please note that in my case I intended the floating element to be shifted on the right side. If you want it on the left please use left instead of right.


In my experience, the best way to handle this was to kick the element out of the parent element.

position: sticky didn’t work for me because it didn’t stay fixed on scroll, and position:absolute wasn’t satisfying because it took it out of the document flow.

What I did simply was put the element at the same level as the parent element (so that they are siblings). That way it’s fixed above the element and isn’t taken out of the flow. No additional complicated css or js required.


In this case, you can change the positions of the fixed element according to its parent element.

you can get the position of your parent element using the following code.

function coordinatesSetter() {
    var elm = $('parentElement');
    var rect = elm.getBoundingClientRect(),
    scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
    scrollTop = window.pageYOffset || document.documentElement.scrollTop;

    return {top: rect.top + scrollTop, left: rect.left + scrollLeft});
 }
0

精彩评论

暂无评论...
验证码 换一张
取 消