在与position: sticky
玩了一段时间后,我开始实现它的粘性导航,并遇到了这个有趣但令人沮丧的滚动弹跳问题。
这是在许多站点上看到的常见类型的导航行为,您通常使用javascript来计算页面中相对元素的偏移量。当元素到达窗口顶部时,将添加一个“卡住”类,使用position: fixed
将元素从文档流中取出,并在其位置添加相同高度的虚拟元素以防止页面“跳跃”。此外,通常会看到javascript然后缩小导航的高度以在滚动时节省空间。
CSS现在似乎用position: sticky
处理所有这些,除了(据我所知),检测元素何时“卡住”。相反,我使用了一些javascript进行卡住检测,发现一切都很好,直到粘性元素的高度需要改变。
这很难解释,但它会对生产造成严重破坏 - 所以这里有一个简单的例子,我已经尽可能简单地说明了这个问题。
CSS sticky position height adjustment bug
当页面的高度恰好是正确的长度时,最好说明一下,所以我在元素上设置了一个固定的高度,以确保每个人都能看到相同的东西。您可以添加更多内容,滚动浏览时仍然存在问题。
结果是一些非常奇怪的行为。当向下滚动时,导航棒,当收缩导航栏时,浏览器自动创建position: sticky
的“虚拟元素”似乎与它保持同步。这意味着,当添加卡住的类时,整个页面变得更小,稍后一小段时间,导航不再卡住,从而导致一个小故障的振动循环。
我测试的每个浏览器的行为也完全不同。在chrome中,这种弹跳永远无法解决,它会在无限循环中不断添加/删除卡住的类。更有趣的是,在Safari中,滚动位置被“推回”到不会出错的状态。然后在Firefox中,在强制滚动位置再次恢复之前,它会执行这两个操作,一两秒钟的故障。
我想知道是否有人经历过这个,并想出任何解决方案?我提出的任何js解决方法都没有真正起作用或者非常好!当人气越来越高时,会有更多的人打到这个......
天才的解决方案,黑客,见解或完美的解决方案都欢迎!
(显然你需要更多的声誉来评论而不是答案......)
这似乎是一个合法的布局错误,所以我很好奇浏览器贡献者的意见可能是什么。提出了Chromium和Firefox bug跟踪器中的问题,看看会发生什么:
https://bugs.chromium.org/p/chromium/issues/detail?id=734461 https://bugzilla.mozilla.org/show_bug.cgi?id=1374171
这是我想出的一个解决方法,它在视觉上给出了相同的效果。
似乎将transform
而不是height
与position: sticky
一起过渡工作得很好。你没有得到常数类切换。
因此,如果我们想要将导航的高度减半,我们可以通过将scaleY
从1更改为0.5来将其压缩一半
这反过来又扼杀了我们的链接,所以我们然后将它们扩展到原始大小的两倍以抵消压扁,将scaleY
从1调整到2。
我们要做的最后一个修复是将导航区域翻译到页面顶部以补偿较小的高度。
片段在下面。这里的关键部分如下:
nav {
transform: scaleY(1) translateY(0);
}
nav a {
transform: scaleY(1);
}
nav.stuck {
transform: scaleY(0.5) translateY(-50%);
}
nav.stuck a {
transform: scaleY(2);
}
nav, nav a {
transition: all 0.6 ease-in-out;
}
前两条规则并不是绝对必要的,但我希望包括一个前后只是为了使事情更清楚。
nav = document.querySelector('nav');
section = document.querySelector('section');
function supportSticky() {
if(window.CSS && CSS.supports) {
return CSS.supports("(position: sticky)") || CSS.supports("(position: -webkit-sticky)");
} else {
var el = document.createElement("div");
el.style.position = "sticky";
return el.style.position == "sticky";
}
}
function handleScroll() {
function isStuck(el) {
return el.offsetTop - section.scrollTop <= 0 ? true : false;
}
isStuck(nav) ? nav.classList.add("stuck") : nav.classList.remove("stuck");
}
if (supportSticky()) section.addEventListener('scroll', handleScroll);
html,
body,
h1 {
margin: 0;
font-family: arial;
}
section {
width: 100%;
max-width: 600px;
margin: 0px auto;
box-shadow: 0 1px 7px #ccc;
height: 378px;
overflow-y: scroll;
}
header {
padding: 3em;
}
nav {
display: flex;
width: 100%;
background-color: #ddd;
justify-content: center;
padding: 3em;
box-sizing: border-box;
position: sticky;
top: 0;
transition: all .6s ease-in-out;
transform: scaleY(1) translateY(0);
}
nav.stuck {
background-color: red;
transform: scaleY(0.5) translateY(-50%);
}
nav.stuck a {
transform: scaleY(2);
}
nav a {
text-decoration: none;
color: #fff;
padding: 1ch 1em;
background-color: #bbb;
margin-right: 1em;
border-radius: 3px;
transition: all .6s ease-in-out;
}
nav a:hover {
background-color: #aaa;
}
article {
padding: 3em;
}
<section>
<header>
<h1>CSS sticky position height adjustment bug</h1>
</header>
<nav>
<a href="">Item 1</a>
<a href="">Item 2</a>
<a href="">Item 3</a>
<a href="">Item 4</a>
</nav>
<article>
<h1>Sticky navigation</h1>
<p>The navigation above should shrink when it gets to the top.</p>
<h1>There is no 'stuck feature' in CSS</h1>
<p>So we need javascript to work that out, and set a stuck class.</p>
<h1>But it bounces!</h1>
<p>Because the dummy element is kept in sync with the nav height...</p>
</article>
</section>
尝试同样的事情后,我可以确认这是一个问题。我在我的标题上使用position sticky并通过JS同时添加一个类(触发一些动画,这些动画改变了高度,如上面描述的CodePen)
var header = document.getElementById("header");
var sticky = header.offsetTop;
window.onscroll = function () {
if (window.scrollY > sticky) {
header.classList.add("stuck");
} else {
header.classList.remove("stuck");
}
};
事实上,高度变化会使窗口高度变得混乱,并且当它变小1px时会触发删除动画的else。删除动画会将高度更改回原始大小,然后循环重新开始。
我想知道如何在没有原生stuck
元素/ class / pseudo的情况下正确编码