【问题标题】:Why does changing the height of a 'position:sticky' element alter the scroll position on Chrome but not Safari?为什么更改 'position:sticky' 元素的高度会改变 Chrome 上的滚动位置,而不是 Safari?
【发布时间】:2020-06-07 18:17:48
【问题描述】:

我注意到 Chrome 和 Safari 对元素的 position: sticky 处理略有不同。

具体来说,当粘性元素变得更高,并且当前滚动窗口以使粘性元素偏离其初始位置时,Chrome 会将 scrollTop 位置更改相同的量 - 呈现内容的外观当粘性元素在顶部生长时保持静止。

在 Safari 中,scrollTop 的位置在这种情况下保持不变,使内容看起来向下移动以适应粘性元素增加的高度。

我在下面创建了一个代码 sn-p 来演示浏览器在这种情况下的行为。上面的截图显示了这个演示在每个浏览器上的表现,但你可以在这里自己尝试一下。

function grow() {
  const header = document.getElementById("header");
  document.getElementById("header").classList.toggle("large-header");
  updateScrollText();
}

function updateScrollText() {
	const container = document.getElementById("container");
	const scrollParent = getScrollParent(container);
  document.getElementById("scrollbarpos1").innerHTML = scrollParent.scrollTop;
  document.getElementById("scrollheight1").innerHTML = scrollParent.scrollHeight;
  document.getElementById("containerheight1").innerHTML = container.offsetHeight;
  document.getElementById("scrollbarpos2").innerHTML = scrollParent.scrollTop;
  document.getElementById("scrollheight2").innerHTML = scrollParent.scrollHeight;
  document.getElementById("containerheight2").innerHTML = container.offsetHeight;
}

function getScrollParent(node) {
  if (node == null) {
    return null;
  }

  if (node.scrollHeight > node.clientHeight) {
    return node;
  } else {
    return getScrollParent(node.parentNode);
  }
}


window.onscroll = updateScrollText; 
window.onload = updateScrollText;
#header {
  background-color: #CACACA;
  position: sticky;
  top: 0;
  padding: 20px;
}

.large-header {
  height: 100px;
}

.content {
  background-color: #a2a6c4;
  height: 1500px;
}

.shift-down {
  margin-top: 50px;
}
<div id="container">
  <div id="header">
    <button type="button" onclick="grow()">Grow/Shrink</button>
  </div>
  <div class="content">
    <br>
    Scrollbar position: <span id="scrollbarpos1">0</span>
    <br>
    Scroll height: <span id="scrollheight1">0</span>
    <br>
    Container height: <span id="containerheight1">0</span>
    <br>
    <br>
    Voluptatibus omnis perspiciatis consequatur magni error exercitationem saepe qui. Ipsa sint non labore voluptates. Asperiores aut non ullam aut sit omnis ducimus in. Aut enim nihil unde ad expedita. Ratione necessitatibus quasi dolorem sunt aperiam nobis ducimus.
Sequi quasi maiores eos aut non. Ipsam delectus sit facilis aut. Dolor facilis eum dignissimos. Vero reiciendis odio quis blanditiis.
Error nesciunt rem facilis. Neque labore et qui sequi eos corrupti dolorem. Reprehenderit qui voluptatem et neque ducimus ipsum similique fugit. Ea sint alias qui laborum nesciunt. Nihil ex repellendus odit sint unde fuga.
A eum nulla ut cumque necessitatibus culpa exercitationem unde. Corrupti sit minima eveniet et aut possimus sapiente. Est accusantium aut ut numquam illo.
Praesentium fugit pariatur eum ad velit distinctio culpa id. Quia voluptatum dignissimos consequatur. Eaque nihil voluptas in voluptas voluptas eius voluptas.
    <br>
    <br>
    
    <div class="shift-down">
    Scrollbar position: <span id="scrollbarpos2">0</span>
    <br>
    Scroll height: <span id="scrollheight2">0</span>
    <br>
    Container height: <span id="containerheight2">0</span>
    </div>
  </div> 
</div>

我查看了W3C spec on Positioned Layout,但找不到任何具体定义它应该如何工作的东西。

所以我的问题是:

  • 为什么这两种浏览器的行为不同?
  • 如果有的话,哪一个是“正确的”?
  • 有什么方法可以使两个浏览器的行为相同(无论哪种方式)?

【问题讨论】:

  • @RobertoVargas 也许我遗漏了一些东西,但这看起来像是一个不同的问题。它根本没有提到改变粘性元素的高度或滚动位置。
  • > 有没有办法让两个浏览器的行为相同(无论哪种方式)?嗨,克雷格,谢谢你问这个问题——我很惊讶,因为它没有出现太多。我也对解决方案很感兴趣 - 我正在考虑用 js 修补差异,但是,我不确定它会有多成功(这也意味着强大和实用)。

标签: html css css-position sticky


【解决方案1】:

为什么这两种浏览器的行为不同?

要理解这一点,您首先需要了解粘性元素如何占用页面空间。当您滚动经过粘性元素时,粘性元素的渲染部分会跟随视口的顶部,但该元素仍会在物理上占用它最初放置的空间:

当您调整标题大小时,这会使浏览器处于一种奇怪的状态,因为它实际上是一个您已经滚动过去的元素。有两种方法可以解决这个问题。您可以像 Safari 和 Firefox 一样,始终与文档顶部保持相同的距离,或者您可以像 Chromium 一样移动视口,使其跟随您当前正在查看的元素:

我相信 Safari 和 Firefox 会按照自己的方式来做,因为它是最简单的解决方案,而且每个浏览器一直都是这样做的。 我相信 Chromium 最近改变了它的方法,因为当用户阅读加载缓慢且在加载后会改变大小的广告的文章时,它具有明显的优势:

正如您在上面看到的,当广告在 Safari 和 Firefox 中加载时,它会导致页面内容发生重大变化,这会让用户感到厌烦和迷失方向。如果有几个广告紧接着加载,这可能尤其糟糕。 使用 Chromium 方法,可以调整视口,以便用户甚至不会注意到发生了任何事情。

我找不到任何规范或讨论来支持我的主张,但我很确定这就是背后的原因。我不确定 Firefox 或 Safari 是否有理由不实施 Chromiums 方法,或者他们只是觉得它没有那么重要。

如果有的话,哪一个是“正确的”?

哪种解决方案最好可能是一个非常复杂且有些主观的讨论,我不会深入讨论,尽管 Chromiums 方法的好处是不可否认的。

有没有办法让两个浏览器的行为相同(无论哪种方式)?

当您按下Grow/Shrink按钮时,您可以随时检测滚动高度是否发生变化,然后在更改后立即将其设置回来。您也可以做相反的事情并自己更改滚动高度,以便所有浏览器的行为都像 Chromium。这是一个完全这样做的人的旧文章: https://simonerescio.it/en/2017/03/chrome-changes-its-center-of-gravity-reference-is-not-the-document-but-the-viewport

【讨论】:

  • 我从thetiby 中发现了一个反对 Chrome 行为的论点:“在用户向下滚动 1px 后高度缩小 1px 的粘性标题将导致粘性标题的滚动父级向上滚动在 Chrome 中为 1px,将粘性标题重置为之前更大的高度,因为它现在位于其父元素的顶部。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多