如何确定 CSS 元素是否粘在顶部或底部?

问题描述 投票:0回答:1

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>

javascript sticky
1个回答
0
投票

当顶部粘性元素与视口顶部边缘的距离达到其

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>

© www.soinside.com 2019 - 2024. All rights reserved.