解决移动端下拉刷新会触发浏览器自带下拉刷新的问题

3,450 阅读1分钟

在做移动端的H5页面时,由于有些手机或浏览器自有的特性,整个页面具有下拉刷新功能。但如果我们的需求是下拉获取下一页的数据,不希望下拉刷新,但同时也想保留整个页面的正常上下滚动,该如何做呢?

尝试过禁用body上的touchmove事件,但是禁用后发现到整个页面都不能滑动了,所以转变思路。

实现要点 

  1. 给body元素设置高度,并设置overflow: hidden (这是不触发浏览器自带下拉刷新的关键,但离开页面时,记得还原,不然会影响其他页面的正常滚动)
  2. 给滚动区域设置固定高度,并设置overflow: scroll
  3. 如果要求完美,比如iPhone11等全面屏手机,有些小可爱会把搜索栏设置在底部,这时候会遮挡到页面,如果要解决这个问题,可以给滚动div设置成固定定位 position: fixed;

上代码

    /** 消息列表 */
    const MessageList = () => {
      const [_list, setList] = useState<Message[]>([])
      const [_info, setInfo] = useStates<any>(INIT_DATA)
      /** 记住上一次的高度 */
      const [_lastHeight, setLastHeight] = useState<number>(0)
      const ref = useRef<any>()
      const onRefresh = () => {
        return getList()
      }

      const getList = async () => {
      try {
          const { data } = await MessageService.getMessage({ message_system_type: "h5", page: _info.page })
          if (!data || !data?.lists?.length) {
            _info.showEmptyCard = true
            setInfo(_info)
            return
          }
          if (_info.page == 1) {
            setList(data?.lists?.reverse())
          } else {
            setList(reverseAry(data?.lists))
          }
          _info.page = _info.page + 1
          if (data?.lists?.length < 15) {
            _info.disable = true
          }
          setInfo(_info)
          // window.scrollTo(0, ref.current.offsetHeight - _lastHeight)
          document.getElementById("sPage").scrollTo(0, ref.current.offsetHeight - _lastHeight) //因为我们是在父级div中滚动,所以控制滚动距离就不能用window了
          setLastHeight(ref.current.offsetHeight)
        } catch {}
      }

      /** 消息反转 */
      const reverseAry = (ary: any[]) => {
        const tempArr = [...ary]
        tempArr.reverse()
        const newArr = [...tempArr, ..._list]
        return newArr
      }

      const wait = (ms: number) => new Promise((resolve: any) => setTimeout(() => resolve(), ms))

      const init = autoLoading(async () => {
        await getList()
        await wait(400)
        // window.scrollTo(0, ref.current?.offsetHeight) 
        document.getElementById("sPage").scrollTo(0, ref.current?.offsetHeight) //因为我们是在父级div中滚动,所以控制滚动距离就不能用window了
      })

      useEffect(() => {
        init()
        return () => {
          INIT_DATA.page = 1
          INIT_DATA.showEmptyCard = false
          setInfo(INIT_DATA)
        }
      }, [])
      
      useEffect(() => {
        document.body.style.overflow = "hidden" //进入页面时给body添加行类样式 overflow:hidden
        return () => {
          document.body.style.overflow = "visible" //离开页面时给body还原 overflow 为默认值
        }
      }, [])

      return (
        <PullToRefresh disable={_info.disable} onRefresh={onRefresh}>
          <Visible visible={_info.disable}>
            <div className={styles.noMore}>- 没有更多消息了 -</div>
          </Visible>
          <Visible visible={!!_list?.length}>
            <div className={styles.box} ref={ref}>
              {_list?.map((item) => (
                <MessageCard {...item} key={item.id} />
              ))}
            </div>
          </Visible>
          <Visible visible={_info.showEmptyCard}>
            <Empty />
          </Visible>
        </PullToRefresh>
      )
    }

    export default MessageList