如何将粘性元素放置在其锚定的目标元素上方?
需求描述
当用户悬停页面上的目标元素(本文中为p元素)时,需显示按钮。按钮位置需为距离目标元素底部50px或视口底部50px,取两者中位置更高的:
- 元素完全可见时,按钮(偏绿色圆形)显示在目标元素底部下方50px处;
- 用户向上滚动导致目标元素部分移出视口时,按钮不超出视口,改为显示在距离视口底部50px处。
已尝试方案
我认为这是position: sticky的适用场景,首次尝试代码如下:
.large { display: block; height: 130px; background-color: #e6e6e6ff; } .target { background-color: #b3b3b3ff; } .sticky { position: sticky; bottom: 30px; height: 20px; width: 20px; background-color: #008080ff; }
<p class="large">Lorem ipsum dolor...</p> <p class="large target">...sit amet consectetur...</p> <div class="sticky"></div> <p class="large">...adipiscing elit.</p>
但遇到问题:设置按钮为sticky后,它会处于目标元素的下方正常流位置,而我需要按钮覆盖在目标元素的区域内。
我尝试用嵌套结构解决:
.large { display: block; height: 130px; background-color: #e6e6e6ff; } .target { background-color: #b3b3b3ff; } .sticky { position: sticky; bottom: 30px; height: 20px; width: 20px; background-color: #008080ff; } .overlay { /* 尝试让div.overlay与div.container完全重合 */ position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; }
<p class="large">Lorem ipsum dolor...</p> <!-- 期望div.container能紧密包裹p元素"...sit amet consectetur..." --> <div class="container"> <p class="large target">...sit amet consectetur...</p> <div class="overlay"> <!-- 将粘性元素放在overlay中,希望它能覆盖在p元素上方 --> <div class="sticky"></div> </div> </div> <p class="large">...adipiscing elit.</p>
期望外层div包裹p元素,overlay类的div与外层div尺寸一致,使按钮能覆盖在上方,但发现粘性元素放入绝对定位元素内无法正常工作。
更新:JavaScript实现方案
以下是实现了预期效果的代码片段,我通过JavaScript完成了功能,现寻求纯CSS解决方案(若存在):
$(document).ready( function() { var mouseX; var mouseY; $(document).mousemove(function(event) { mouseX = event.pageX; mouseY = event.pageY - document.documentElement.scrollTop; }); function mouseInRect(rect) { return ( rect.x <= mouseX && mouseX <= rect.x + rect.width && rect.y <= mouseY && mouseY <= rect.y + rect.height ); } function showButton(toggle) { if (toggle) { $("#button").css("display", "block"); } else { $("#button").css("display", "none"); } } $("#target").mouseenter(function() { showButton(true); }); $("#target").mouseleave(function() { showButton(false); }); $(document).scroll(function() { var viewportHeight = document.documentElement.clientHeight; var targetRect = $("#target")[0].getBoundingClientRect(); var targetBottom = viewportHeight - targetRect.bottom; /* 按钮元素应显示在距离目标元素底部20px 或 视口底部20px的位置,取两者中更高的 */ $("#button").css("bottom", Math.max(targetBottom + 20, 20)); /* 用户滚动可能导致鼠标指针离开目标元素但未触发mouseleave事件,因此在滚动处理程序中重新计算是否处于悬停状态 */ showButton(mouseInRect(targetRect)); }); } );
.large { display: block; height: 130px; background-color: #e6e6e6ff; } .target { background-color: #b3b3b3ff; } .sticky { display: none; position: fixed; height: 20px; width: 20px; left: 20px; background-color: #008080ff; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <p class="large">Lorem ipsum dolor...</p> <p id="target" class="large target">...sit amet consectetur...</p> <div id="button" class="sticky"></div> <p class="large">...adipiscing elit.</p>
纯CSS解决方案
可以通过父容器嵌套、position: sticky结合负边距和hover伪类实现,无需JavaScript:
.large { display: block; height: 130px; background-color: #e6e6e6ff; } /* 目标元素的父容器,设置相对定位+负边距抵消按钮空间 */ .target-container { position: relative; margin-bottom: -30px; /* 抵消按钮高度+间距,避免撑开布局 */ } .target { background-color: #b3b3b3ff; } .button { position: sticky; bottom: 50px; /* 视口底部最小间距 */ height: 20px; width: 20px; background-color: #008080ff; display: none; left: 20px; margin-top: 30px; /* 距离目标元素底部50px:50px总间距 - 按钮自身20px高度 */ } /* 悬停目标元素时显示按钮 */ .target-container:hover .button { display: block; }
<p class="large">Lorem ipsum dolor...</p> <div class="target-container"> <p class="large target">...sit amet consectetur...</p> <div class="button"></div> </div> <p class="large">...adipiscing elit.</p>
原理说明
- 父容器布局控制:父容器包裹目标元素和按钮,负边距抵消按钮占据的空间,避免按钮在正常流中额外撑开布局;
- 粘性定位逻辑:按钮的
position: sticky结合bottom:50px保证它不会超出视口底部,margin-top:30px让目标元素完全可见时,按钮处于其底部下方50px位置; - 悬停显示控制:通过父容器的
hover伪类直接控制按钮的显示/隐藏,无需JS监听鼠标事件。
内容的提问来源于stack exchange,提问作者defab67
相关产品推荐
相关产品推荐

