当
position:sticky
元素卡住时,我需要将 CSS 类应用到该元素。 当我使用顶部位置时,我可以正常工作,但我无法弄清楚如何确定底部位置。 我想我需要在某个地方考虑高度,但我只是不确定这里最好的是什么。
我还需要它来处理偏移,只是
0
的顶部或底部位置。这是我到目前为止所拥有的
const stickyElements = [...document.querySelectorAll(".sticky")];
window.addEventListener("scroll", () => {
stickyElements.forEach((el) => toggleClassIfStuck(el))
});
window.dispatchEvent(new Event('scroll')); //trigger initially
function toggleClassIfStuck(el){
const computedStyles = getComputedStyle(el);
if (this.canBeStuck(computedStyles)) {
const hasTopPositionSet = computedStyles.top !== 'auto';
const hasBottomPositionSet = computedStyles.bottom !== 'auto';
if (hasTopPositionSet || hasBottomPositionSet) {
el.classList.toggle('is-stuck', this.isStuck(el, computedStyles, hasBottomPositionSet))
}
}
}
function canBeStuck(computedStyles) {
return computedStyles.display !== 'none' && computedStyles.position === 'sticky';
}
function isStuck(el, computedStyles, shouldUseBottomPosition) {
const offsetParent = el.offsetParent; //the element which this element is relatively sticky to
const rect = el.getBoundingClientRect();
const parentRect = offsetParent.getBoundingClientRect();
if (shouldUseBottomPosition) {
//this isn't correct, but not sure what to check here!
const elBottom = parseInt(computedStyles.bottom, 10);
return rect.top - rect.bottom === elBottom;
} else {
const elTop = parseInt(computedStyles.top,10);
return rect.top === elTop;
}
}
.sticky {
position:sticky;
background: #EEE;
padding: .5rem;
border: 1px solid #DDD;
transition: all 200ms;
}
.sticky-top { top:0; }
.sticky-top-offset { top: 1rem;}
.sticky-bottom { bottom: 0; }
.sticky-bottom-offset { bottom: 1rem; }
.is-stuck{
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.25);
background: lightskyblue;
}
main{ display: flex; gap:.5rem;}
section{ height:120vh; width: 40%; }
<main>
<section>
<br>
<div id="one" class="sticky sticky-top">Top</div>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<div class="sticky sticky-bottom">Bottom</div>
<br>
</section>
<section>
<br><br><br><br>
<div class="sticky sticky-top-offset">Top with offset</div>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<div class="sticky sticky-bottom-offset">Bottom with offset</div>
<br><br><br><br>
</section>
</main>
当顶部粘性元素与视口顶部边缘的距离达到其
top
值时,它就会被卡住。
底部粘性元素的概念类似;当距视口底部边缘的距离达到其
bottom
值时,它们就会被卡住。
您只需要找出该距离,即可通过从
rect.bottom
中减去 window.innerHeight
来实现:
const elBottomViewportDistance = window.innerHeight - rect.bottom;
const stickyElements = [...document.querySelectorAll(".sticky")];
window.addEventListener("scroll", () => {
stickyElements.forEach((el) => toggleClassIfStuck(el))
});
window.dispatchEvent(new Event('scroll')); //trigger initially
function toggleClassIfStuck(el){
const computedStyles = getComputedStyle(el);
if (this.canBeStuck(computedStyles)) {
const hasTopPositionSet = computedStyles.top !== 'auto';
const hasBottomPositionSet = computedStyles.bottom !== 'auto';
if (hasTopPositionSet || hasBottomPositionSet) {
el.classList.toggle('is-stuck', this.isStuck(el, computedStyles, hasBottomPositionSet))
}
}
}
function canBeStuck(computedStyles) {
return computedStyles.display !== 'none' && computedStyles.position === 'sticky';
}
function isStuck(el, computedStyles, shouldUseBottomPosition) {
const offsetParent = el.offsetParent; //the element which this element is relatively sticky to
const rect = el.getBoundingClientRect();
const parentRect = offsetParent.getBoundingClientRect();
if (shouldUseBottomPosition) {
const elBottom = parseInt(computedStyles.bottom, 10);
return window.innerHeight - rect.bottom <= elBottom;
} else {
const elTop = parseInt(computedStyles.top,10);
return rect.top <= elTop;
}
}
.sticky {
position:sticky;
background: #EEE;
padding: .5rem;
border: 1px solid #DDD;
transition: all 200ms;
}
.sticky-top { top:0; }
.sticky-top-offset { top: 1rem;}
.sticky-bottom { bottom: 0; }
.sticky-bottom-offset { bottom: 1rem; }
.is-stuck{
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.25);
background: lightskyblue;
}
main{ display: flex; gap:.5rem;}
section{ height:120vh; width: 40%; }
<main>
<section>
<br>
<div id="one" class="sticky sticky-top">Top</div>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<div class="sticky sticky-bottom">Bottom</div>
<br>
</section>
<section>
<br><br><br><br>
<div class="sticky sticky-top-offset">Top with offset</div>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
<div class="sticky sticky-bottom-offset">Bottom with offset</div>
<br><br><br><br>
</section>
</main>