粘贴位置:可滚动,比视口长

Pet*_*lka 18 javascript css sticky

如果元素position: sticky"卡住"并且比视口长,则只能在滚动到容器底部后才能看到其内容.

如果"卡住"元素与文档一起滚动并且一旦到达其底部边缘就停止,那将会很酷.如果用户向后滚动,同样的事情会再次发生,但相反.

例子

TLDR; 有一个库(StickyKit)可以完成我正在寻找的东西,但是在新的异步滚动中表现不佳.

JSFiddle with StickyKit - https://jsfiddle.net/cibulka/4nd3b0tt/ - (这就是我所描述的,但表现不佳,见下文)

JSFiddle与原生 position: sticky - https://jsfiddle.net/cibulka/np6afe9c/1/ - https://jsfiddle.net/cibulka/pxz6e0qv/ - (这不)

背景

很长一段时间,我是StickyKit的快乐用户.不幸的是,它在异步滚动方面效果不佳,越来越多的浏览器使用异步滚动来提高性能.例如,使用新的Firefox Quantum(57),StickyKit几乎无法使用.

我在StickyKit Github中创建了一个问题,但作者似乎放弃了包:https://github.com/leafo/sticky-kit/issues/252

因此,我不得不弃用StickyKit并转向原生position:sticky(使用StickyFill进行polyfilled).不幸的是,有一些事情position:sticky是做不到的,这就是其中之一.

我还有另一个问题position:sticky:位置粘滞:叠加

我正在寻找什么

一个建议,基本上,如何处理这个问题.我想使用不同的JS/jQuery库,编写自己的代码或使用一些古怪的CSS hack来破解position:sticky功能.

Aus*_*tin 7

我已经接受了 jnns 答案并更新了它,以便它在滚动之间像粘性套件一样平滑。唯一的问题是,它需要一个包含 div 的幻数,容器在 div 绝对定位时保持其大小 - 这可以在代码中通过 css 变量来解决。

window.onscroll = function (e) {
  if (window.scrollY < this.prevScrollY) {
    // Track position state of nav
    // 1 == stuck to top
    // 0 == absolute positioning
    // -1 == stuck to bottom 
    this.stick_pos = scrollUpwards(this.stick_pos);
  } else {
    this.stick_pos = scrollDownwards(this.stick_pos);
  }
  this.prevScrollY = window.scrollY; 
}

function scrollUpwards(stick_pos) {
  // If the element is already stuck to the top then we are fine
  if(stick_pos === 1) return stick_pos;
  // Figure out where the new window will be after scroll
  let aside = $("aside").get(0);
  let aboveAside = aside.getBoundingClientRect().top > 0;
  // If we are going above the element then we know we must stick
  // it to the top
  if (aboveAside){
    $("aside").css("position", "sticky")
      .css("top", 0)
      .css("bottom", '')
      .css("align-self", "flex-start");
    return 1;
  }
  // If it will still be below the top of the element, then we
  // must absolutely position it to its current position - if it already is absolutely positioned then we do nothing
  if (stick_pos == 0) return stick_pos;
  
  // Undo the stick to the bottom
  // First get the current position
  $("aside")
    .css("top", aside.offsetTop)
    .css("position", "absolute")
    .css("bottom", '')
    .css("align-self", "");
  return 0;
}

function scrollDownwards(stick_pos) {
  /*
  let aside = $("aside").get(0);
  let aboveAside = aside.offsetTop >= window.scrollY;
  let browser_bottom = window.scrollY + window.innerHeight;
  let aside_bottom = aside.offsetTop + aside.offsetHeight;
  let belowAside = browser_bottom >= aside_bottom;
  if (aboveAside) {
    //console.log("stick to bottom");
    $("aside").css("top", ''); 
    $("aside").css("bottom", 0); 
    $("aside").css("align-self", "flex-end");
  }
  */
  // If the element is already stuck to the bottom then we are fine
  if(stick_pos === -1) return stick_pos;
  // Figure out where the new window will be after scroll
  let aside = $("aside").get(0);
  let browser_bottom = window.innerHeight;
  let aside_bottom = aside.getBoundingClientRect().top + aside.offsetHeight;
  let belowAside = browser_bottom > aside_bottom;
  // If we are going below the element then we know we must stick
  // it to the bottom.
  if (belowAside){
    $("aside").css("position", "sticky")
      .css("top", '')
      .css("bottom", 0)
      .css("align-self", "flex-end");
    return -1;
  }
  // If it will still be above the bottom of the element, then we
  // must absolutely position it to its current position - if it already is absolutely positioned then we do nothing
  if (stick_pos == 0) return stick_pos;
  
  // Undo the stick to the top
  // First get the current position
  // $("aside").css("position", "absolute")
  // .css("top", aside.offsetTop);
  $("aside")
    .css("top", aside.offsetTop)
    .css("position", "absolute")
    .css("bottom", '')
    .css("align-self", "");
  return 0;
}
Run Code Online (Sandbox Code Playgroud)
div#section {
  /* begin: irrelevant styling */
  margin: 5em auto;
  padding: 0.625rem;
  max-width: 300px;
  font-family: sans-serif;
  font-size: 18px;
  line-height: 1.5em;
  text-align: justify;
  background-color: #dbe4ee;
  /* end: irrelevant styling */
  display: flex;
  justify-content: space-around;
}
div#section div#nav-container {
  position: relative;
  display: flex;
  min-width: 2em;
}
div#section div#nav-container aside {
  position: sticky;
  align-self: flex-start;
  /* begin: irrelevant styling */
  background-color: #81a4cd;
  color: white;
  text-align: center;
  width: 2em;
}
div#section div#nav-container aside div {
  padding: 0 .3em;
}
div#section article {
  margin-left: 0.5em;
}
div#section article p {
  margin: 0;
}
div#section article p + p {
  margin-top: 1.5em;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='section'>
  <div id='nav-container'>
  <aside>
    <div>A</div>
    <div>B</div>
    <div>C</div>
    <div>D</div>
    <div>E</div>
    <div>F</div>
    <div>G</div>
    <div>H</div>
    <div>I</div>
    <div>J</div>
    <div>K</div>
    <div>L</div>
    <div>M</div>
    <div>N</div>
    <div>O</div>
    <div>P</div>
    <div>Q</div>
    <div>R</div>
    <div>S</div>
    <div>T</div>
    <div>U</div>
    <div>V</div>
    <div>W</div>
    <div>X</div>
    <div>Y</div>
    <div>Z</div>
  </aside>
  </div>
  <article>
    <p>Perferendis ut iusto voluptatem ex temporibus aut autem amet. Sit vero in soluta. Est officia asperiores tenetur vel quam nostrum eum facere. Sed totam quasi libero at facilis doloremque. Non aut velit odio. Tempora dolore sunt recusandae sed quia
      sunt.</p>

    <p>Voluptatem optio asperiores dolorem voluptatem. Ipsa alias perspiciatis doloribus est nisi ut. Fuga aut et vitae consequatur dolor corrupti aut minima.</p>

    <p>Facilis et ut eligendi. Excepturi labore asperiores vero. Perferendis porro sunt molestiae. In sit dolorem eum esse sit inventore est. Atque perspiciatis commodi nihil.</p>

    <p>Consequatur ipsa id repellendus voluptatem perspiciatis temporibus. Praesentium eveniet nemo laudantium inventore similique impedit nihil esse. Maiores iste commodi molestiae quas odit nihil ex corrupti. Illum id amet non vero.</p>

    <p>Voluptas soluta itaque et. Aperiam quasi sint eos ullam. Assumenda facilis omnis alias numquam. Odio quia esse vel et minima soluta architecto. Qui saepe consequatur aut rerum. Et et aut voluptatibus inventore.</p>

    <p>Perferendis ut iusto voluptatem ex temporibus aut autem amet. Sit vero in soluta. Est officia asperiores tenetur vel quam nostrum eum facere. Sed totam quasi libero at facilis doloremque. Non aut velit odio. Tempora dolore sunt recusandae sed quia sunt.</p>

    <p>Voluptatem optio asperiores dolorem voluptatem. Ipsa alias perspiciatis doloribus est nisi ut. Fuga aut et vitae consequatur dolor corrupti aut minima.</p>

    <p>Facilis et ut eligendi. Excepturi labore asperiores vero. Perferendis porro sunt molestiae. In sit dolorem eum esse sit inventore est. Atque perspiciatis commodi nihil.</p>

    <p>Consequatur ipsa id repellendus voluptatem perspiciatis temporibus. Praesentium eveniet nemo laudantium inventore similique impedit nihil esse. Maiores iste commodi molestiae quas odit nihil ex corrupti. Illum id amet non vero.</p>

    <p>Voluptas soluta itaque et. Aperiam quasi sint eos ullam. Assumenda facilis omnis alias numquam. Odio quia esse vel et minima soluta architecto. Qui saepe consequatur aut rerum. Et et aut voluptatibus inventore.</p>
</div>
Run Code Online (Sandbox Code Playgroud)


Pin*_*ice 5

有一个名为Sticky Sidebar 的库正是为了解决这个问题而创建的。

Codepen 中的演示,没有粘性侧边栏

Codepen 中带有粘性侧边栏的演示

  1. sticky-sidebar.min.js在您的页面上包含库

  2. 您的 DOM 应该大致如下所示:

    <div class="main-content">
        <div class="sidebar">
            <div class="sidebar__inner">
                <!-- Content goes here -->
            </div>
        </div>
        <div class="content">
            <!-- Content goes here -->
        </div>
    </div>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后你可以像这样初始化

    var sidebar = new StickySidebar('.sidebar', {
      topSpacing: 16,
      bottomSpacing: 16,
      containerSelector: '.content',
      innerWrapperSelector: '.sidebar__inner'
    });
    
    Run Code Online (Sandbox Code Playgroud)


Pra*_*ant 0

你看过Scrollama吗

它使用新的Intersection Observer Web API,基本上是浏览器告诉 JS 某个元素何时出现在视口内,而 JS 不必监听滚动事件。position: sticky因此,对于以高性能方式在 JS 中实现类似行为非常有用。