假设我有一个侧面导航,我希望侧面导航保持粘性,以便它与用户一起滚动。但是,侧面导航的高度不是固定的:当用户在导航上打开带有子项目的项目时,它会垂直扩展以显示子项目。
一旦子菜单展开,或者我想在小屏幕上显示,菜单可能无法像以前一样在一个屏幕上显示。用户的解决方案是向下滚动页面以查看导航的底部,然后选择他们选择的链接。但是,由于侧面菜单是粘滞的,因此向下滚动时,菜单将与其一起滚动。只有当用户到达内容的底部时,菜单才会找到其容器的末端并停留,以便用户可以访问菜单的底部。
这是我叉出的code pen来说明问题。内容很大,侧面导航仅比屏幕高度稍大。
.sidebar {
width: 25%;
height: 120vh;
min-height: 400px;
overflow: auto;
position: -webkit-sticky;
position: sticky;
top: 3em;
}
.main {
width: 60%;
height: 2000vh;
min-height: 1000px;
display: flex;
flex-direction: column;
position: relative;
}
.main,
.sidebar {
border: 5px solid #222;
background-color: white;
border-radius: 10px;
color: #222;
padding: 15px;
}
.wrapper {
display: flex;
justify-content: space-between;
}
body {
padding: 3%;
background-color: #ccc;
font-size: 20px;
box-sizing: border-box;
font-family: Lato, sans-serif;
}
code, pre {
background-color: #ccc;
padding: 0 3px;
border-radius: 5px;
}
.bottom {
position: absolute;
bottom: 0;
}
<div class="wrapper">
<div class="main">
<h2>Main content</h2>
<p>Scroll down the page!</p>
<p class='bottom'>You made it!</p>
</div>
<div class="sidebar">
<h3>Sticky sidebar</h3>
<p>I will follow you! But should wait til you hit my bottom when scrolling down before following down. If you are scrolling up, I should wait for you to hit my top before following.</p>
</div>
</div>
我该怎么办?由于性能原因,我会喜欢纯CSS版本。我不想限制导航的高度,因为这意味着绝大多数用户将需要通过直接在导航上滚动而不是像以往那样在内容上滚动来重新考虑他们如何使用导航。
我发现了一个名为sticky kit的JS库,它可以产生我想要的确切效果,尤其是可滚动的粘性元素示例。但是,它已经有两年没有更新了,我不想依靠一个jquery插件,该插件可能不会随着网络标准的改变而起作用。
有一个中间选项,其中我使用JS来更改滚动时CSS的粘滞方向-如this pen。但是,当您切换方向时,会有一个非常愚蠢的事情。
答案 0 :(得分:0)
因此,根据我的研究,目前这还不是粘性规范的一部分-您需要使用一些JS。
但是,如果您改为使用JS控制粘性定位,则可以将JS最小化。我分叉了我在问题末尾发布的JS笔,并对其进行了研究,以找到一种使用很少的JS的最小解决方案,从而试图避免在滚动事件侦听器上进行过多的计算。
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;
}
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;
width: 2em; /* Magic number, must be same width as the aside nav */
/* begin: irrelevant styling */
background-color: #81a4cd;
color: white;
text-align: center;
}
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;
}
<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>
与CSS解决方案相比,它并不理想,但它相当活泼,并且看起来很稳定。我渴望改进,并且会在codepen上进行清理。一种解决方案是利用幻数(旁容器和旁容器的宽度必须保持相同,因为旁容器在临时滚动时绝对定位)。我想这可以通过使用CSS变量来解决。