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

如何仅用React实现页面滚动时内部内容优先滚动逻辑

React实现区块内优先滚动逻辑

核心逻辑

  • 监听全局滚动,判断当前是否处于目标区块(如示例中的B区块)
  • 进入目标区块后,优先让内部子元素滚动;当内部子元素滚到顶/底时,恢复页面正常滚动,继续切换到上下区块
  • 通过阻止页面默认滚动行为,将滚动事件“转移”到内部容器

代码实现

组件代码

import { useState, useEffect, useRef } from 'react';

const PageScroll = () => {
  const [isInBlockB, setIsInBlockB] = useState(false);
  const blockBRef = useRef(null);
  const blockBInnerRef = useRef(null);
  let lastScrollTop = 0;

  useEffect(() => {
    const handleScroll = (e) => {
      const scrollTop = window.scrollY;
      const blockB = blockBRef.current;
      const blockBInner = blockBInnerRef.current;

      if (!blockB || !blockBInner) return;

      // 检测是否进入B区块的可视范围
      const blockBRect = blockB.getBoundingClientRect();
      const isEntered = blockBRect.top <= 0 && blockBRect.bottom >= window.innerHeight;
      setIsInBlockB(isEntered);

      if (isEntered) {
        const scrollDirection = scrollTop > lastScrollTop ? 'down' : 'up';
        const innerScrollTop = blockBInner.scrollTop;
        const innerMaxScroll = blockBInner.scrollHeight - blockBInner.clientHeight;

        if (scrollDirection === 'down') {
          // 向下滚动:内部未滚到底时,阻止页面滚动,驱动内部滚动
          if (innerScrollTop < innerMaxScroll) {
            e.preventDefault();
            blockBInner.scrollTop += scrollTop - lastScrollTop;
          }
        } else {
          // 向上滚动:内部未滚到顶时,阻止页面滚动,驱动内部滚动
          if (innerScrollTop > 0) {
            e.preventDefault();
            blockBInner.scrollTop += scrollTop - lastScrollTop;
          }
        }
      }

      lastScrollTop = scrollTop;
    };

    // 必须设置passive: false才能阻止默认滚动行为
    window.addEventListener('scroll', handleScroll, { passive: false });

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div className="page-container">
      <div className="block block-a">区块A</div>
      <div ref={blockBRef} className="block block-b">
        <div ref={blockBInnerRef} className="block-b-inner">
          <div className="block-b-child">子区块B1</div>
          <div className="block-b-child">子区块B2</div>
          <div className="block-b-child">子区块B3</div>
          <div className="block-b-child">子区块B4</div>
        </div>
      </div>
      <div className="block block-c">区块C</div>
    </div>
  );
};

export default PageScroll;

配套样式

.page-container {
  width: 100%;
}

.block {
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
}

.block-a {
  background-color: #f0f0f0;
}

.block-b {
  background-color: #e0e0e0;
  overflow: hidden;
}

.block-b-inner {
  height: 100%;
  overflow-y: auto;
  width: 100%;
}

/* 可选:隐藏内部滚动条 */
.block-b-inner::-webkit-scrollbar {
  display: none;
}

.block-b-child {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid #ccc;
}

.block-c {
  background-color: #d0d0d0;
}

关键注意点

  • 监听滚动事件时必须设置{ passive: false },否则无法调用e.preventDefault()阻止页面滚动
  • 通过getBoundingClientRect()准确判断是否进入目标区块的可视范围
  • 内部滚动容器需设置height: 100%和overflow-y: auto,确保能独立滚动
  • 借助lastScrollTop记录上一次滚动位置,区分滚动方向,处理上下滚动的不同逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:27:39