使用CSS在React渲染的组件上阻止滚动。

46

因此,我通过React在我的html中呈现一个组件,像这样:

 <html>
  <body>
    <div id=app>${appHtml}</div>
    <script src="/bundle.js"></script>
  </body>
</html>

我的应用程序中有一个汉堡菜单按钮,当点击该按钮时会全屏显示。

然而,内容可滚动。通常我会为 body 标签添加一个类并使其 overflow: hidden 以防止这种情况发生。但是,我的 React 组件是在 body 标签内呈现的,因此我无法根据 React 组件内的点击来控制切换类。

有人有任何想法/建议可以帮助我实现所需的功能吗?

谢谢!


link:-https://www.npmjs.com/package/react-sticky - Razia sultana
9个回答

82

"我无法控制基于React组件内部点击切换类的操作。"

这并不一定是正确的!

很好,你正在以“React方式”思考,并谨慎修改DOM。避免进行DOM操作的主要原因是它会导致React尝试渲染的内容与您尝试进行的未知更改之间发生冲突。但在这种情况下,您没有操作React正在呈现的DOM,而是操作其父级。在这种情况下,您完全可以像这样操作:

document.body.style.overflow = "hidden"

或者

document.body.classList.add("no-scroll")

或任何其它可行的方法。你完全安全,因为React仅渲染#app中的DOM,而不关心其父元素中发生的事情。实际上,许多应用程序和网站仅在页面的一小部分使用React,以渲染单个组件或小部件,而非整个应用程序。

除此之外,还有一种更好、更“React式”的方法来实现你想要的效果。只需重新构建你的应用程序,使滚动容器位于React应用程序内部,而不是body。结构可能看起来像这样:

<html>
  <body>
    <div id="app">
      <div id="scroll-container">
        <!-- the rest of your app -->
      </div>
    </div>
  </body>
</html>

使 body 溢出隐藏,并且使 body#app 填满整个屏幕,而在 React 中完全可以控制是否允许 #scroll-container 滚动。


谢谢!我之前还以为 React 会在你试图在组件外部更改任何内容时报错。我从来不知道你仍然可以这样做。 - pourmesomecode
在 Next.js 中没有 DOM。 - Egon Stetmann.

8

以上方法在iOS移动设备上不起作用。

body-scroll-lock 使用CSS和JS的组合来使其在所有设备上都能工作,并保持目标元素(例如模态框)的可滚动性。

也就是说,对于iOS,需要检测到达目标元素底部或顶部时,完全停止滚动。


2
这个回答怎么解决了提问者的问题? - Raul Sauco
body-scroll-lock 能否让你在子元素滚动到底部或顶部时防止父元素滚动? - tonix
@RaulSauco 我自己也不得不进行一些尝试,可以看看我在这个问题中的答案。 - Fiddle Freak
尝试过使用 body-scroll-lock,但对我没有起作用。 - János
https://stackoverflow.com/questions/74971048/how-to-prevent-scrolling-of-body-which-works-also-in-ios-mobile - János

4
使用React.useEffect()钩子来禁用特定页面上的滚动,然后进行清理。 例如:
useEffect(() => {
    document.body.style.overflow = "hidden";
    return () => (document.body.style.overflow = "scroll");
});

3

以下是如何在Reactjs中使用body-scroll-lock库的方法...

import React, {useState} from 'react';
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';

// Components
import Hamburger from './HeaderComponents/Hamburger.js';
import MegaDropdown from './HeaderComponents/MegaDropdown.js';

const Header = props => {
  // variables
  const [isSideNavShown, setIsSideNavShown] = useState(false);
  const [isDropdownShowing, setIsDropdownShowing] = useState(false);

  isDropdownShowing ? disableBodyScroll(document) : enableBodyScroll(document)

  return (
    <header className="header">
      <Hamburger isDropdownShowing={isDropdownShowing} setIsDropdownShowing={setIsDropdownShowing} />
      <MegaDropdown isDropdownShowing={isDropdownShowing} setIsDropdownShowing={setIsDropdownShowing} />
    </header>
  )
}

export default Header;

特别是这一行代码:isDropdownShowing ? disableBodyScroll(document) : enableBodyScroll(document)

当下拉菜单中的内容填满屏幕并需要添加滚动条时,该代码可以禁用窗口(body)滚动,同时下拉菜单显示(覆盖整个屏幕)。因此,即使窗口(body)滚动被禁用,我仍然可以通过下拉菜单进行滚动。


1
这是我的建议:你可以将包含内容的 div 的高度设置为 100%(height: 100%),或者在这种情况下设置 body { height: 100% }。

1
我建议使用CSS的 "sticky" 定位,但我猜这不是问题所在。但它对我有用。其他解决方案可能更好。

1

在React 16.8+中,使用Hooks和函数式组件实现这一点的非常简单的方法是使用useEffect来执行副作用。

  useEffect(() => {
    const html = document.querySelector("html");
    if (html) {
      html.style.overflow = state.isMenuOpen ? "hidden" : "auto";
    }
  }, [state.isMenuOpen]); // condition to watch to perform side effect

0

在 Next.JS 中,您可以使用:

const html = document.querySelector('html');
if (html) {
  html.style.overflow = 'hidden';
}

你可以使用以下代码进行重置:

const html = document.querySelector('html');
if (html) {
  html.style.overflow = 'auto';
}

0
编写了一个小钩子来处理页面主体的滚动。
import { useState, useEffect } from "react";

export default function useSetBodyScroll() {
  const [bodyScroll, setBodyScroll] = useState(true);

  useEffect(() => {
    const resetOnResize = () => {
      if (window.innerWidth <= 1023) document.body.style.overflow = "hidden";
      if (window.innerWidth >= 1024) document.body.style.overflow = "scrolls";
    };

    if (!bodyScroll) {
      document.body.style.overflow = "hidden";
    } else {
      document.body.style.overflow = "scroll";
      window.addEventListener("resize", resetOnResize);
    }

    return () => {
      window.removeEventListener("resize", resetOnResize);
    };
  }, [bodyScroll]);

  return setBodyScroll;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接