为什么更改 'position:sticky' 元素的高度会改变 Chrome 而不是 Safari 上的滚动位置?
Why does changing the height of a 'position:sticky' element alter the scroll position on Chrome but not Safari?
我注意到 Chrome 和 Safari 对 position: sticky
元素的处理略有不同。
具体来说,当粘性元素变高时,window 当前滚动,使得粘性元素偏离其初始位置,Chrome 将改变 scrollTop
相同数量的位置 - 让内容的外观保持静止,而粘性元素在顶部生长。
在 Safari 中,scrollTop
位置在这种情况下没有改变,呈现内容向下移动以适应粘性元素增加的高度的外观。
我在下面创建了一个代码片段来演示浏览器在这种情况下的行为。上面的屏幕截图显示了这个演示在每个浏览器上的表现,但你可以在这里自己试一试。
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,但找不到任何具体定义它应该如何工作的东西。
所以我的问题是:
- 为什么这两种浏览器的行为不同?
- 如果有的话,"correct"是哪一个?
- 有什么方法可以使两种浏览器的行为相同(无论哪种方式)?
Why is this behaviour different on these two browsers?
要理解这一点,您首先需要了解粘性元素如何在页面上占据 space。当您滚动经过粘性元素时,粘性元素的渲染部分会跟随视口的顶部,但该元素在物理上仍占据其最初放置的位置 space:
当您调整 header 大小时,这会使浏览器处于一种奇怪的情况,因为它实际上是您已经滚动过去的元素。有两种方法可以解决这个问题。您可以像 Safari 和 Firefox 一样,始终与文档顶部保持相同的距离,或者您可以像 Chromium 一样移动视口,使其跟随您当前正在查看的元素:
我相信 Safari 和 Firefox 会按照自己的方式来做,因为这是最简单的解决方案,而且每个浏览器一直都是这样做的。
我相信 Chromium 最近改变了它的方法,因为当用户阅读带有缓慢加载广告的文章时它具有明显的优势,广告在加载后会改变大小:
正如您在上面看到的,当广告在 Safari 和 Firefox 中加载时,它会导致页面内容发生重大变化,这会让用户感到厌烦和迷失方向。如果有多个广告紧接着加载,这尤其糟糕。
使用 Chromium 方法,调整视口,以便用户甚至不会注意到发生了什么。
我找不到任何规范或讨论来支持我的主张,但我很确定这就是背后的原因。我不确定 Firefox 或 Safari 是否有理由不实施 Chromiums 方法,或者他们是否只是觉得它不那么重要。
Which one, if any, is "correct"?
尽管 Chromium 方法的好处是不可否认的,但哪种解决方案最好可能是一个非常复杂且有些主观的讨论,我不会深入讨论。
Is there any way to make both browsers behave identically (either way)?
当您按下Grow/Shrink
按钮时,您可以随时检测滚动高度是否发生变化,然后在发生变化后立即将其设置回来。您也可以反其道而行之,自己更改滚动高度,以便所有浏览器的行为都像 Chromium。这是一篇来自某人的旧文章,他正是这样做的:
https://simonerescio.it/en/2017/03/chrome-changes-its-center-of-gravity-reference-is-not-the-document-but-the-viewport
我注意到 Chrome 和 Safari 对 position: sticky
元素的处理略有不同。
具体来说,当粘性元素变高时,window 当前滚动,使得粘性元素偏离其初始位置,Chrome 将改变 scrollTop
相同数量的位置 - 让内容的外观保持静止,而粘性元素在顶部生长。
在 Safari 中,scrollTop
位置在这种情况下没有改变,呈现内容向下移动以适应粘性元素增加的高度的外观。
我在下面创建了一个代码片段来演示浏览器在这种情况下的行为。上面的屏幕截图显示了这个演示在每个浏览器上的表现,但你可以在这里自己试一试。
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,但找不到任何具体定义它应该如何工作的东西。
所以我的问题是:
- 为什么这两种浏览器的行为不同?
- 如果有的话,"correct"是哪一个?
- 有什么方法可以使两种浏览器的行为相同(无论哪种方式)?
Why is this behaviour different on these two browsers?
要理解这一点,您首先需要了解粘性元素如何在页面上占据 space。当您滚动经过粘性元素时,粘性元素的渲染部分会跟随视口的顶部,但该元素在物理上仍占据其最初放置的位置 space:
当您调整 header 大小时,这会使浏览器处于一种奇怪的情况,因为它实际上是您已经滚动过去的元素。有两种方法可以解决这个问题。您可以像 Safari 和 Firefox 一样,始终与文档顶部保持相同的距离,或者您可以像 Chromium 一样移动视口,使其跟随您当前正在查看的元素:
我相信 Safari 和 Firefox 会按照自己的方式来做,因为这是最简单的解决方案,而且每个浏览器一直都是这样做的。 我相信 Chromium 最近改变了它的方法,因为当用户阅读带有缓慢加载广告的文章时它具有明显的优势,广告在加载后会改变大小:
正如您在上面看到的,当广告在 Safari 和 Firefox 中加载时,它会导致页面内容发生重大变化,这会让用户感到厌烦和迷失方向。如果有多个广告紧接着加载,这尤其糟糕。 使用 Chromium 方法,调整视口,以便用户甚至不会注意到发生了什么。
我找不到任何规范或讨论来支持我的主张,但我很确定这就是背后的原因。我不确定 Firefox 或 Safari 是否有理由不实施 Chromiums 方法,或者他们是否只是觉得它不那么重要。
Which one, if any, is "correct"?
尽管 Chromium 方法的好处是不可否认的,但哪种解决方案最好可能是一个非常复杂且有些主观的讨论,我不会深入讨论。
Is there any way to make both browsers behave identically (either way)?
当您按下Grow/Shrink
按钮时,您可以随时检测滚动高度是否发生变化,然后在发生变化后立即将其设置回来。您也可以反其道而行之,自己更改滚动高度,以便所有浏览器的行为都像 Chromium。这是一篇来自某人的旧文章,他正是这样做的:
https://simonerescio.it/en/2017/03/chrome-changes-its-center-of-gravity-reference-is-not-the-document-but-the-viewport