如何使一个 div 在滚动到屏幕顶部时固定在顶部?

429

我想创建一个div,它位于内容块下方,但一旦页面滚动到足够接触其顶部边界,它就会固定在原地并随着页面滚动。


6
截至2014年6月,Sticky-kit jQuery插件是最容易上手的选择之一,提供了极低的门槛和丰富的功能。如果您正在寻找快速入门的简单方法,这是一个很好的起点。 - user456584
2
CSS技巧:http://css-tricks.com/scroll-fix-content/ - Ciro Santilli OurBigBook.com
63
在2017年1月,添加CSS position: sticky; top:0; 在大多数浏览器中都能正常工作。 - Colin 't Hart
26
天啊,那个 position: sticky; 的东西真是神奇。 - tscizzle
可能是因为使用了 display flex,请参考此链接:https://dev59.com/9FcP5IYBdhLWcg3w39o-#66966273 - Zohab Ali
在一个relative容器中,position: sticky; top:0;对我有效。 - Marzieh Mousavi
25个回答

363

你可以简单地使用CSS,将你的元素定位为fixed

.fixedElement {
    background-color: #c0c0c0;
    position:fixed;
    top:0;
    width:100%;
    z-index:100;
}

编辑:当滚动偏移量到达元素时,应将该元素位置设置为absolute,然后更改为fixed,并将顶部位置设置为零。

您可以使用scrollTop 函数来检测文档顶部的滚动偏移量:

$(window).scroll(function(e){ 
  var $el = $('.fixedElement'); 
  var isPositionFixed = ($el.css('position') == 'fixed');
  if ($(this).scrollTop() > 200 && !isPositionFixed){ 
    $el.css({'position': 'fixed', 'top': '0px'}); 
  }
  if ($(this).scrollTop() < 200 && isPositionFixed){
    $el.css({'position': 'static', 'top': '0px'}); 
  } 
});

当滚动偏移量达到200时,该元素将会在浏览器窗口顶部固定,因为它的定位是fixed。


9
这并没有达到我的目的。我希望该元素从页面顶部向下200像素开始(为其他内容留出空间),然后一旦用户向下滚动,它就固定在页面顶部。 - evanr
3
您的编辑确实满足了问题的需求,但当页面再次滚动回顶部时仍会出现问题。您可以在到达元素的scrollTop之后将其存储在某个地方,在页面再次到达该位置(向上滚动时)将CSS更改回默认值...可能最好使用.toggleClass来完成这个操作... - Sander
9
基本上这就是当滚动窗口返回到顶部时,我需要删除固定定位的内容。if ($(this).scrollTop() < 200 && $el.css('position') == 'fixed') { $('.fixedElement').css({'position': 'static', 'top': '0px'}); } - Derrick Petzold
1
@DerrickPetzold 我把那个放到答案里了,非常重要的东西 :) - MarioDS
1
你给的 new example 链接太干净清晰了,我甚至都看不到它!-_- - sohaiby
显示剩余3条评论

254

你在Google Code的问题页面上看到了这个例子,最近也出现在Stack Overflow的编辑页面上。

CMS的答案没有在向上滚动时恢复位置。以下是从Stack Overflow不要脸地偷来的代码:

function moveScroller() {
    var $anchor = $("#scroller-anchor");
    var $scroller = $('#scroller');

    var move = function() {
        var st = $(window).scrollTop();
        var ot = $anchor.offset().top;
        if(st > ot) {
            $scroller.css({
                position: "fixed",
                top: "0px"
            });
        } else {
            $scroller.css({
                position: "relative",
                top: ""
            });
        }
    };
    $(window).scroll(move);
    move();
}
<div id="sidebar" style="width:270px;"> 
  <div id="scroller-anchor"></div> 
  <div id="scroller" style="margin-top:10px; width:270px"> 
    Scroller Scroller Scroller
  </div>
</div>

<script type="text/javascript"> 
  $(function() {
    moveScroller();
  });
</script> 

这里有一个简单的在线演示

一种新出现的、无需使用脚本的替代方案是position: sticky,它在Chrome、Firefox和Safari中都得到了支持。请参考HTML5Rocks上的文章以及演示Mozilla文档


3
由于某些原因,在jQuery 1.6.2中使用{scroll:false}会出现问题,似乎没有使用它更好。从链接的演示中[Fork]。有没有想法它是否有作用? - Eddie
2
这似乎很好用,当我使用与演示相同的jQuery版本(1.3.2)时。在某个时候,offset必须已经停止接受对象作为输入http://api.jquery.com/offset/. @Eddie 您的修改应该在当前的jQuery中是安全的。 - Graeme
1
你有没有什么理由不能用var d = $(“#sidebar”).offset()。top;替换var d = $(“#scroller-anchor”).offset()。top;并完全摆脱空的滚动锚点div?这是我的fork,演示了我所说的。 - Mike Deck
有人能看一下这个吗:我在解决它的问题上遇到了困难:http://jsfiddle.net/Csf9S/ - Batman
@Josh,你可以完全摆脱条件if(st <= ot) {},因为它总是成立的...我fork了你的fiddle:http://fiddle.jshell.net/34df1mj7/show/light/ 如果你同意,我会更新你的答案。 - Felix Geenen
显示剩余4条评论

159

截至2017年1月和Chrome 56的发布,大多数常用浏览器都支持CSS中的position: sticky属性。

#thing_to_stick {
  position: sticky;
  top: 0;
}

在Firefox和Chrome中,这对我很有用。

在Safari中,您仍然需要使用position: -webkit-sticky

Internet Explorer和Edge可以使用Polyfills; https://github.com/wilddeer/stickyfill似乎是一个不错的选择。


7
今天常见的大多数浏览器都支持此功能。请参考 caniuse.com/#feat=css-sticky。 其次,我更喜欢能够简化为两行代码的解决方案,而不是需要自定义 JavaScript 的版本。而且它也是具有未来性的。如果你担心浏览器兼容性问题,可以使用 polyfill。 - Colin 't Hart
2
我想补充一下这个评论,我们可以添加z-index: 99999;,否则当它到达顶部时,其他内容将首先被呈现。但是这应该是被接受的解决方案。 - Dayán Ruiz
只需将其包装在id="thing_to_stick"的div中。 - Anton
1
“px”这一部分对我无效,让我以为粘性定位无法生效。正确的方式是 top: 0; - Akaisteph7

35

以下是没有使用jQuery的方法(更新:请查看其他答案,现在可以只用CSS完成此操作)

var startProductBarPos=-1;
window.onscroll=function(){
  var bar = document.getElementById('nav');
  if(startProductBarPos<0)startProductBarPos=findPosY(bar);

  if(pageYOffset>startProductBarPos){
    bar.style.position='fixed';
    bar.style.top=0;
  }else{
    bar.style.position='relative';
  }

};

function findPosY(obj) {
  var curtop = 0;
  if (typeof (obj.offsetParent) != 'undefined' && obj.offsetParent) {
    while (obj.offsetParent) {
      curtop += obj.offsetTop;
      obj = obj.offsetParent;
    }
    curtop += obj.offsetTop;
  }
  else if (obj.y)
    curtop += obj.y;
  return curtop;
}
* {margin:0;padding:0;}
.nav {
  border: 1px red dashed;
  background: #00ffff;
  text-align:center;
  padding: 21px 0;

  margin: 0 auto;
  z-index:10; 
  width:100%;
  left:0;
  right:0;
}

.header {
  text-align:center;
  padding: 65px 0;
  border: 1px red dashed;
}

.content {
  padding: 500px 0;
  text-align:center;
  border: 1px red dashed;
}
.footer {
  padding: 100px 0;
  text-align:center;
  background: #777;
  border: 1px red dashed;
}
<header class="header">This is a Header</header>
<div id="nav" class="nav">Main Navigation</div>
<div class="content">Hello World!</div>
<footer class="footer">This is a Footer</footer>


4
谢谢!现在越来越难找到原生JS的解决方案了。这个方法完美地解决了我的问题。 - Sherri
非常感谢,这个解决方案非常完美,因为jQuery与我的项目中的一些遗留企业代码发生了冲突。 - sng
虽然我遇到了一个问题,即如果我滚动到页面底部,它会自动将我弹回到顶部。 - sng
@sng 我的样本页面是否可以做到这一点? - Jim W says reinstate Monica
@JimW,我找到了问题所在。我想要在主内容div左侧使用垂直菜单栏,但是当您向下滚动时,它会出现错误,因为无法正确确定何时触底。最终,我添加了一行代码来设置容器div的高度为窗口屏幕大小,以解决滚动事件监听器的问题。 - sng
这个解决方案存在问题,因为内容会“跳动”。如果使用较小的padding-top,就更容易看到。演示:https://jsbin.com/fivugux/edit?html,css,js,output - Mosh Feu

33

我曾经和你一样遇到了同样的问题,最终我制作了一个jQuery插件来解决它。实际上,它解决了人们在这里列出的所有问题,还添加了一些可选功能。

选项

stickyPanelSettings = {
    // Use this to set the top margin of the detached panel.
    topPadding: 0,

    // This class is applied when the panel detaches.
    afterDetachCSSClass: "",

    // When set to true the space where the panel was is kept open.
    savePanelSpace: false,

    // Event fires when panel is detached
    // function(detachedPanel, panelSpacer){....}
    onDetached: null,

    // Event fires when panel is reattached
    // function(detachedPanel){....}
    onReAttached: null,

    // Set this using any valid jquery selector to 
    // set the parent of the sticky panel.
    // If set to null then the window object will be used.
    parentSelector: null
};

https://github.com/donnyv/sticky-panel

演示:http://htmlpreview.github.io/?https://github.com/donnyv/sticky-panel/blob/master/jquery.stickyPanel/Main.htm


1
嘿!谢谢你!这是一个很好的解决方案,感谢你的分享,肯定节省了我很多时间。这应该是这个问题的总体接受解决方案,因为据我所读,它是最完整的解决方案。基本上,其他人没有解决应用position:fixed样式后块的原始X位置的问题。你的解决了这个问题。真的非常感谢! - Marcos Buarque
嘿,唐尼,我也喜欢你的插件(v1.4.1)...但是我遇到了一个问题,如果没有指定宽度,块元素会丢失宽度。所以在分离时进行了修改...只需设置宽度即可保持不变。 code// 分离面板 node.css({ "margin": 0, "left": nodeLeft, "top": newNodeTop, "position": "fixed", "width": node.width() });code - Will Hancock
寻找并尝试了许多解决方案,这个“开箱即用”。太棒了!谢谢! - AJ Tatum
2
@WillHancock 我已经添加了iPad支持,修复了刷新错误并添加了onDetached和onReattached事件。 新的事件将在面板分离和重新附加后为您提供对面板和spacerpanel的访问权限。 - Donny V.
4
还新增了parentSelector选项,以支持滚动的div。 - Donny V.
只要容器在窗口中,它就会保持粘性,当容器向上滚动时,粘性也会消失。 - Siraj Alam

21

最简单的解决方案(不需要js): 演示

.container {
  position: relative;
}
.sticky-div {
    position: sticky;
    top: 0;
}
<div class="container">
  <h1>
    relative container & sticky div
  </h1>
  <div class="sticky-div"> this row is sticky</div>
  <div>
    content
  </div>
</div>

1
不需要 position: relative - Mosh Feu

11

这是我使用jQuery实现的方法。这个解决方案是从Stack Overflow上的各种答案中拼凑而成的。该解决方案缓存选择器以提高性能,并解决了粘性div变为sticky时出现的“跳跃”问题。

在jsfiddle上查看:http://jsfiddle.net/HQS8s/

CSS:

.stick {
    position: fixed;
    top: 0;
}

JS:

$(document).ready(function() {
    // Cache selectors for faster performance.
    var $window = $(window),
        $mainMenuBar = $('#mainMenuBar'),
        $mainMenuBarAnchor = $('#mainMenuBarAnchor');

    // Run this on scroll events.
    $window.scroll(function() {
        var window_top = $window.scrollTop();
        var div_top = $mainMenuBarAnchor.offset().top;
        if (window_top > div_top) {
            // Make the div sticky.
            $mainMenuBar.addClass('stick');
            $mainMenuBarAnchor.height($mainMenuBar.height());
        }
        else {
            // Unstick the div.
            $mainMenuBar.removeClass('stick');
            $mainMenuBarAnchor.height(0);
        }
    });
});

10
Josh LeeColin 't Hart所说,你可以选择只使用position: sticky; top: 0;应用于你想要滚动的div...
此外,唯一需要做的就是将其复制到页面顶部或格式化以适应外部CSS表格:
<style>
#sticky_div's_name_here { position: sticky; top: 0; }
</style>

只需将#sticky_div's_name_here替换为您的div的名称,即如果您的div是<div id="example">,则应该放置#example { position: sticky; top: 0; }


7

这里还有另一种选择:

JAVASCRIPT

var initTopPosition= $('#myElementToStick').offset().top;   
$(window).scroll(function(){
    if($(window).scrollTop() > initTopPosition)
        $('#myElementToStick').css({'position':'fixed','top':'0px'});
    else
        $('#myElementToStick').css({'position':'absolute','top':initTopPosition+'px'});
});

你的#myElementToStick应该以position:absolute CSS属性开始。


1
我认为这是一个非常干净和简单的解决方案。我只是不会将元素的位置设置为“absolute” - 这可能会破坏布局 - 我只会将其设置为静态的。 - ESP32

2
对于那些在其他版本中遇到问题的人,这里有一个更多尝试的版本。它结合了在这个重复的问题中讨论的技术,并动态生成所需的帮助器DIV,因此不需要额外的HTML。
CSS:
.sticky { position:fixed; top:0; }

JQuery:

function make_sticky(id) {
    var e = $(id);
    var w = $(window);
    $('<div/>').insertBefore(id);
    $('<div/>').hide().css('height',e.outerHeight()).insertAfter(id);
    var n = e.next();
    var p = e.prev();
    function sticky_relocate() {
      var window_top = w.scrollTop();
      var div_top = p.offset().top;
      if (window_top > div_top) {
        e.addClass('sticky');
        n.show();
      } else {
        e.removeClass('sticky');
        n.hide();
      }
    }
    w.scroll(sticky_relocate);
    sticky_relocate();
}

为了使一个元素粘性,可以执行以下操作:
make_sticky('#sticky-elem-id');

当元素变为粘性时,代码会管理剩余内容的位置,以防止其跳入粘性元素留下的间隙中。当向上滚动到粘性元素之上时,它还将粘性元素返回到其原始的非粘性位置。

你的方法和 JohnB 的方法 非常相似。考虑到你与那个答案的不同之处,我想知道 (1) 使用第二个 "helper div" 是否有优势(而不是只使用一个像 JohnB 一样),以及 (2) 使用 hide() 和 show() 是否比仅设置 helper div 的高度更有优势 (像 JohnB 所做的)?也许会有性能差异?到目前为止,我还没有能够分辨出差异,但也许某些情况下会有差异 (例如可能涉及内联元素之类的东西),这就是我问的原因。谢谢。 - Jason Frank

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