You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vanilla ES6 滚动固定区块后窗口大小调整问题求助

Fixing Window Resize Issues for Scroll-Stuck Sections in Vanilla ES6

Got it, let's work through this problem. When implementing scroll-stuck sections in plain ES6, window resizes can throw off your position calculations because the dimensions and offsets of your elements change as the viewport size shifts. Here's a robust, performant solution tailored to your HTML structure:

Step 1: Complete & Structure Your HTML

First, let's finish up that partial HTML you shared (adding a proper footer for context):

<header class="forewords">
  <h1>Lorem some</h1>
</header>
<div class="wrapper">
  <section class="project" id="item1">this is section 1</section>
  <section class="project" id="item2">this is section 2</section>
  <section class="project" id="item3">this is section 3</section>
</div>
<footer class="page-footer">
  <p>Footer content here</p>
</footer>

Step 2: Base CSS for Sticky Behavior

Add styles to define your sections' default state and the "stuck" fixed state:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.forewords {
  padding: 2rem;
  background: #f0f0f0;
}

.wrapper {
  position: relative;
  min-height: calc(100vh - 8rem); /* Adjust based on header/footer height */
}

.project {
  height: 100vh;
  padding: 2rem;
  border-bottom: 1px solid #ddd;
  background: #fff;
}

/* Class for when a section is fixed to the top */
.project.stuck {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: auto; /* Keep content height, or set to 100vh for full viewport */
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  z-index: 10;
}

Step 3: ES6 JavaScript with Resize Handling

This script handles scroll detection, position recalculation on resize, and uses debouncing to avoid performance hits:

// Debounce utility to limit resize event frequency
const debounce = (func, delay = 150) => {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
};

// Track critical element data
let headerHeight;
let sectionData = [];
let activeStuckSection = null;

// Initialize/re-calculate element positions (called on load + resize)
const updateSectionPositions = () => {
  // Get updated header height
  headerHeight = document.querySelector('.forewords').offsetHeight;
  
  // Reset and rebuild section position data
  sectionData = [];
  document.querySelectorAll('.project').forEach(section => {
    const rect = section.getBoundingClientRect();
    sectionData.push({
      element: section,
      topOffset: rect.top + window.scrollY, // Position relative to document top
      height: rect.height
    });
  });
  
  // Re-check scroll position to update stuck state immediately after resize
  checkScrollState();
};

// Determine which section should be stuck based on scroll position
const checkScrollState = () => {
  const scrollPosition = window.scrollY;
  const wrapper = document.querySelector('.wrapper');

  // Loop through sections to find the active one to stick
  sectionData.forEach((section, index) => {
    const nextSection = sectionData[index + 1];
    // Calculate when this section should stick/unstick
    const stickThreshold = section.topOffset - headerHeight;
    const unstickThreshold = nextSection ? nextSection.topOffset - headerHeight : Infinity;

    if (scrollPosition >= stickThreshold && scrollPosition < unstickThreshold) {
      // Only update if the active section changed
      if (activeStuckSection !== section.element) {
        // Remove stuck state from previous section
        if (activeStuckSection) {
          activeStuckSection.classList.remove('stuck');
          wrapper.style.paddingTop = '0';
        }
        // Apply stuck state to current section
        section.element.classList.add('stuck');
        // Add padding to wrapper to fill the gap left by the fixed element
        wrapper.style.paddingTop = `${section.height}px`;
        activeStuckSection = section.element;
      }
    } else if (section.element.classList.contains('stuck')) {
      section.element.classList.remove('stuck');
    }
  });

  // Reset state if scrolled above the first section
  if (scrollPosition < sectionData[0].topOffset - headerHeight && activeStuckSection) {
    activeStuckSection.classList.remove('stuck');
    wrapper.style.paddingTop = '0';
    activeStuckSection = null;
  }
};

// Initialize everything on page load
document.addEventListener('DOMContentLoaded', () => {
  updateSectionPositions();
  window.addEventListener('scroll', checkScrollState);
  // Use debounced resize handler to avoid excessive calculations
  window.addEventListener('resize', debounce(updateSectionPositions));
});

Key Details to Note:

  • Debouncing: The debounce function ensures we don't recalculate positions every single time the resize event fires (which can be hundreds of times per second), keeping the UI smooth.
  • Dynamic Position Updates: updateSectionPositions re-fetches element dimensions and offsets on every resize, so your stuck logic always uses accurate, up-to-date values.
  • Content Jump Prevention: We adjust the wrapper's paddingTop when a section is fixed to avoid sudden layout shifts caused by the fixed element leaving the document flow.

内容的提问来源于stack exchange,提问作者—

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:14:37