我可以相对于父元素将一个元素固定定位吗?

392

我发现当我将一个元素设置为fixed时,父元素是否被设置为relative并不影响它的定位,它会相对于窗口进行fixed定位?

#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


3
正确答案在这里: https://dev59.com/NG445IYBdhLWcg3wQX-G - Soyoes
11
“position:sticky”将会是解决方案。截至2016年4月,火狐浏览器似乎是唯一支持它的浏览器(http://caniuse.com/#feat=css-sticky)。 - bob
在最近的浏览器中(发布于此问题之后),有一个更好的解决方案可用。请参见标记为[2016更新]的答案。 - Patrick McElhaney
10个回答

251

让我回答两种可能的问题。请注意,您现有的标题(和原始帖子)询问的问题与您在编辑和后续评论中寻求的不同。


要使元素相对于父元素"固定",您需要在子元素上使用position:absolute,并在父元素上使用除默认或静态以外的任何位置模式。

例如:

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

这将使childDiv元素相对于父元素定位向左50像素,向下20像素。


要将元素“固定”相对于窗口定位,您需要使用 position:fixed,并可以使用 top:left:right:bottom: 适当地进行定位。

例如:

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

这将使 yourDiv 相对于浏览器窗口固定位置,距离底部和右侧边缘均为40像素。


140
哦,嗯...我实际上想模仿一个粘性按钮,当它被绝对定位时,当我向下滚动时,它不会粘在那里...嗯 - Jiew Meng
10
我会与选择此答案的提问者讨论此事。显然,他稍后对问题进行了编辑。我不急于删除此答案,因为仍有人发现它有用(8个赞),并且回答了问题的标题。如果现在有更好的答案,提问者应该选择另一个被接受的答案。 - DuckMaestro
3
如果在带有滚动条的溢出div中使用position:absolute,它会随着滚动而滚动。 - Szymon Toda
7
尽管这个解决方案可能确实有益于楼主,但它并没有回答标题中提出的问题。下面的答案更准确,应该被标记为正确答案。 - David Vasquez
1
你应该更新你的答案,因为calc函数可以轻松地帮助我们解决这个问题。假设你的主容器最大可以扩展到900像素,对于初始值,你可以设置为right: 0px。在断点命中后,使用以下代码: @media screen and (min-width: 901px) { right: calc((100% - 900px) / 2) }这将再次将这些元素附加到你的主容器上。 - Ertan Kara
显示剩余4条评论

240

CSS规范要求position:fixed相对于视口而不是包含它的定位元素进行定位。

如果你必须相对于父元素指定元素位置,你需要使用JavaScript先查找父元素相对于视口的位置,然后相应地设置子元素(固定位置)的位置。

替代方案:一些浏览器支持sticky CSS,限制元素在其容器和视口内定位。根据提交消息:

  

sticky ...将元素约束在其容器框和视口的交集内部定位。

     

粘性定位的元素的行为类似于position:relative(空间在流中保留),但其偏移量由粘性位置确定。更改isInFlowPositioned()以涵盖相对和粘性。

根据你的设计目标,这种行为在某些情况下可能有所帮助。它目前是一个正在草拟的草案,并且得到了不错的支持,除了表格元素。在Safari中,position: sticky仍然需要一个-webkit前缀。

请参阅caniuse以获取有关浏览器支持的最新统计数据。


3
如果您单击帖子“已编辑”注释后的日期时间,实际上可以查看编辑历史记录。 - JohnK
1
@JohnK 我知道。我所说的“更改了他的设计”,指的是 OP 在网站上使用的最终代码,而不是在此处提出的问题的更改。 - Jon Adams
8
这不是一个好的解决方案,“-webkit-sticky”在Chrome中已不再受支持(除非您启用实验性标志)。它可能会在某个时候回来,但在官方支持之前,最好使用Grawl的解决方案。 - Chuck Le Butt
我们还没有杀死Gecko和Trident。 - Даниил Пронин
4
现在sticky有相当不错的支持,我刚刚更新了这个答案。 - jhpratt
显示剩余3条评论

153

2016 更新

现代浏览器中,可以将一个元素相对于其容器进行固定定位。具有 transform 属性的元素会成为其所有固定定位子元素的视口。

正如CSS 变换模块所述:

对于由 CSS 盒模型控制布局的元素,transform 属性除了 none 之外的任何值都会导致元素为其所有后代建立包含块。其填充框将用于所有绝对定位后代、固定定位后代和后代固定背景附件的布局。

.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>


5
不适用于 IE11。 - T J
2
当子元素具有absolute定位并在具有relativeabsolute定位的父元素中时,为什么需要这样做呢? - Sean Kendle
33
当父元素具有滚动条,并且您希望固定位置的元素保持不动,而父元素在其后面卷动时,就会出现这种情况。 - Patrick McElhaney
4
我通常会在它周围再加一个div,并将其绝对定位到外部div,然后让内部div拥有滚动条。不过,有时候你无法控制HTML,所以知道这个方法很好! - Sean Kendle
4
有几个不同的CSS属性可以产生这种效果,另一个是will-change: transform - Adam Leggett
显示剩余7条评论

64

首先,设置 position: fixedleft: 50%,其次 - 现在你的起点是中心,你可以使用 margin 设置新的位置。


1
非常小心这种方法!如果固定元素不在视口中,它将无法按预期工作。如果视口太短,则此示例演示了该问题。http://jsbin.com/igolur/4/edit - Bill Criswell
1
最佳解决方案,直到更多浏览器更好地支持“sticky”。 - Chuck Le Butt
你可以在具有“left: 50%”(.parent)的元素内部使用附加包装器,并为其子元素(.child)设置“left: -50%”,这样子元素将位于中心。提示:对于.parent,使用pointer-events: none;) - Даниил Пронин

31

这个解决方案对我来说非常有效!参考原始详细答案:https://dev59.com/8mw15IYBdhLWcg3wJ4es#11833892

    
    .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>

    


4
这个答案应该放在顶部。 - MK.
1
非常简单的解决方案。一流! - Nifel

19

我知道这已经很旧了,但在找不到我要寻找的(纯CSS)答案之后,我想出了这个解决方案(部分摘自medium.com),并希望能帮助其他寻求相同内容的人。

如果你结合 @DuckMaestro 的回答,你可以将一个元素固定在父元素(实际上是祖父元素)中。使用 position: absolute; 将元素定位在具有 position: relative; 的父元素内部,然后在绝对定位的元素内部使用 position: fixed;,如下所示:

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;
}

示例

如@JonAdams所说,position: fixed的定义要求元素相对于视口定位,但是您可以使用此解决方案绕过水平方面。

注意:这与仅在固定元素上设置rightleft值不同,因为那样会导致在调整窗口大小时水平移动。


2
这似乎不像你所说的那样起作用。该元素仍然相对于视口定位为“fixed”,而不是相对于包含元素。它的左/右位置没有被改变,因此在这方面它仍然停留在原地,但从本质上讲,它并没有根据其父级固定。看看这个例子,我在父div上面添加了一个div。div.fixed-feedback仍然距离窗口顶部120px。 http://codepen.io/anon/pen/LbvOaY - Sean Kendle
1
@SeanKendle,你说得对。如果元素相对于父元素是真正的“fixed”,那就只是“absolute”。在这种情况下,它在垂直方向上固定在视口上,在水平方向上固定在父元素上。这就是我尝试做的,因为在我看来,这就是问题所问的。不过,我可能是错的。 - Lifehack
我同意你的观点,除了你说父元素中的 absolute 实际上等同于 fixed。因为如果它是 absolute,滚动会导致它随着父 div 的其余内容移动,而不像真正的 fixed 位置那样保持静止。 - Sean Kendle

10

以下是Jon Adams的建议示例,使用jQuery将div(工具栏)固定在页面元素的右侧。思路是找到视口右侧与页面元素右侧之间的距离,并保持工具栏的右侧在那里!

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();
});

16
JQuery的诞生源于应对混乱的局面!随着CSS的成熟和浏览器的兼容性不断提高,希望这一点是正确的。 - KXL

9

这是一篇旧帖,但为了防止有人需要,我将在此留下我的JavaScript解决方案。


// 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>


注释

  1. 我使用了transform: translateY(@px),因为它计算轻便,高性能动画

  2. 我只在现代浏览器中尝试过此功能,它不适用于旧浏览器,在那些浏览器中需要供应商支持(当然包括IE)


2

我知道这是一篇比较旧的文章,但我认为关于Jiew Meng尝试做的事情已经可以在此网站上找到很好的例子。请查看位于此处的侧边菜单:https://stackoverflow.com/faq#questions。浅显地观察一下,我可以告诉你,在滚动条滚动到锚点以下时,javascript会附加一个固定位置,并在滚动条向上超过同一锚点时删除固定位置。希望这能让某人朝着正确的方向开始。


1

通过多个div,我成功地实现了一个固定y轴和绝对x轴的div。 在我的情况下,我需要在左右两侧各有一个div,与中心的1180像素宽度的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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接