草庐IT

关于 javascript:Position sticky: 可滚动,当长于视口时

codeneng 2023-03-28 原文

Position sticky: scrollable, when longer than viewport

当带有 position: sticky 的元素"卡住"并且比视口长时,您只能在滚动到容器底部后才能看到其内容。

如果"卡住"元素随着文档滚动并在到达其底部边缘时停止,那将是很酷的。如果用户向后滚动,同样的事情会再次发生,但相反。

例子

TLDR;有一个库(StickyKit)可以满足我的要求,但在新的异步滚动中表现不佳。

带有 StickyKit 的 JSFiddle - 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 填充)。不幸的是,有几件事 position:sticky 不能做,这就是其中之一。

position:sticky 还有另一个问题:Position sticky: overlay

我在寻找什么

基本上是关于如何解决此问题的建议。我准备使用不同的 JS/jQuery 库,编写自己的代码或使用一些古怪的 CSS hack 来破解 position:sticky 功能。

  • 为了将来参考,我使用了 abouolia.github.io/sticky-sidebar 它就像一个魅力


有一个名为 Sticky Sidebar 的库正是为解决该问题而设计的。

  • 谢谢,这终于解决了我的问题...... 1)我的侧边栏比视口高度更长,2)我需要我的侧边栏底部边缘在向下滚动时停止在视口底部,但在滚动页面时滚动回顶部边缘到向上。 Sticky-kit 插件或 position:sticky css 对我不起作用。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js">

 
 
    A
    B
    C
    D
    E
    F
    G
    H
    I
    J
    K
    L
    M
    N
    O
    P
    Q
    R
    S
    T
    U
    V
    W
    X
    Y
    Z
  </aside>
 
 
    <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>


您可以尝试根据滚动方向使用jQuery从上到下切换粘性元素的锚点和位置,并且仍然使用CSS的原生position: sticky

我让它在这个 codepen 中工作,但没有时间在方向改变时消除跳跃。但也许这对你来说已经足够了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Use display: flex on the container

window.onscroll = function (e) {
  if (window.scrollY < this.prevScrollY) {
    scrollUpwards();
  } else {
    scrollDownwards();
  }
  this.prevScrollY = window.scrollY;
}

function scrollUpwards() {
  $("aside").css("top", 0);
  $("aside").css("bottom", '');
  $("aside").css("align-self","flex-start");

}

function scrollDownwards() {
  $("aside").css("top", '');
  $("aside").css("bottom", 0);
  $("aside").css("align-self","flex-end");
}

你看过 Scrollama

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

  • 这似乎不能解决我的问题,因为它似乎与"scrollytelling"用例有关——根据滚动位置切换 div,而不是滚动它们。不过谢谢!

有关关于 javascript:Position sticky: 可滚动,当长于视口时的更多相关文章

  1. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  2. ruby-on-rails - 关于 Ruby 的一般问题 - 2

    我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia

  3. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

  4. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  5. ruby - 关于 Ruby 中 Dir[] 和 File.join() 的混淆 - 2

    我在Ruby中遇到了一个关于Dir[]和File.join()的简单程序,blobs_dir='/path/to/dir'Dir[File.join(blobs_dir,"**","*")].eachdo|file|FileUtils.rm_rf(file)ifFile.symlink?(file)我有两个困惑:首先,File.join(@blobs_dir,"**","*")中的第二个和第三个参数是什么意思?其次,Dir[]在Ruby中有什么用?我只知道它等价于Dir.glob(),但是,我对Dir.glob()确实不是很清楚。 最佳答案

  6. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  7. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

  8. javascript - jQuery 的 jquery-1.10.2.min.map 正在触发 404(未找到) - 2

    我看到有关未找到文件min.map的错误消息:GETjQuery'sjquery-1.10.2.min.mapistriggeringa404(NotFound)截图这是从哪里来的? 最佳答案 如果ChromeDevTools报告.map文件的404(可能是jquery-1.10.2.min.map、jquery.min.map或jquery-2.0.3.min.map,但任何事情都可能发生)首先要知道的是,这仅在使用DevTools时才会请求。您的用户不会遇到此404。现在您可以修复此问题或禁用sourcemap功能。修复:获取文

  9. ruby-on-rails - 我将 Rails3 与 tinymce 一起使用。如何呈现用户关闭浏览器javascript然后输入xss? - 2

    我有一个用Rails3编写的站点。我的帖子模型有一个名为“内容”的文本列。在帖子面板中,html表单使用tinymce将“content”列设置为textarea字段。在首页,因为使用了tinymce,post.html.erb的代码需要用这样的原始方法来实现。.好的,现在如果我关闭浏览器javascript,这个文本区域可以在没有tinymce的情况下输入,也许用户会输入任何xss,比如alert('xss');.我的前台会显示那个警告框。我尝试sanitize(@post.content)在posts_controller中,但sanitize方法将相互过滤tinymce样式。例如

  10. ruby - 使用 Selenium WebDriver 启用/禁用 javascript - 2

    出于某种原因,我必须为Firefox禁用javascript(手动,我们按照提到的步骤执行http://support.mozilla.org/en-US/kb/javascript-settings-for-interactive-web-pages#w_enabling-and-disabling-javascript)。使用Ruby的SeleniumWebDriver如何实现这一点? 最佳答案 是的,这是可能的。而是另一种方式。您首先需要查看链接Selenium::WebDriver::Firefox::Profile#[]=

随机推荐