如何在 ReactJS 的 onScroll 事件中操作 DOM?

Posted

技术标签:

【中文标题】如何在 ReactJS 的 onScroll 事件中操作 DOM?【英文标题】:How can i manipulate DOM inside the onScroll event in ReactJS? 【发布时间】:2020-03-26 04:21:21 【问题描述】:

我正在尝试创建一个向上箭头按钮,如果我在页面顶部,它将设置为 display: "none",当我进一步向下滚动时,我想将其设置为显示:“阻止” .我怎样才能做到这一点?我试图在我的 handleScroll 函数中操作 DOM,但它不起作用。

我的 TopButton.js 组件

import React from "react";
import "../assets/arrow-up.png";

class TopButton extends React.Component 
  constructor(props) 
    super(props);
    this.handleScroll = this.handleScroll.bind(this);
  

  componentDidMount = () => 
    window.addEventListener("scroll", this.handleScroll, true);
  ;

  handleClick = () => 
    document.body.scrollTop = 0;
    document.documentElement.scrollTop = 0;
  ;

  handleScroll = () => 
    console.log("scroll");

    let x = document.getElementsByClassName("topbutton_button");
    var x = document.getElementsByClassName("topbutton_button");
    // x.style.display = "none";

    // console.log(event.target.className);

    // if (
    //   document.body.scrollTop > 40 ||
    //   document.documentElement.scrollTop > 40
    // ) 
    //   style.display = "block";
    //  else 
    //   display = "none";
    // 
  ;

  render() 
    return (
      <div className="topbutton_container">
        <button
          style= display: "block" 
          onClick=this.handleClick
          onScroll=this.handleScroll
          className="topbutton_button"
        >
          <img src=require("../assets/arrow-up.png") />
        </button>
      </div>
    );
  


export default TopButton;

【问题讨论】:

【参考方案1】:

至少有两个原因它不起作用:

见this question's answers;基本上,getElementsByClassName 返回一个htmlCollection,而不是单个元素,但是您注释掉的代码将其视为单个元素。

如果您的组件曾经被重新渲染,它将以其默认状态渲染,而不是您通过 DOM 更改的更新状态

但这不是你使用 React 的方式。相反,你会:

    将按钮状态(是否应该被阻止)作为状态保存在您的组件中;

    在渲染topbutton_button 时使用该状态,相应地设置其样式或类;和

    在您的 handleScroll 处理程序中更新该状态

其他几点说明:

您还需要在卸载组件时移除处理程序

不应将箭头函数用于组件生命周期函数

您不需要在箭头函数上使用bind(例如handleScroll)。 要么把它变成一个箭头函数要么在构造函数中使用bind来绑定它。

类似的事情,请参阅*** cmets

import React from "react";
import "../assets/arrow-up.png";

// *** Reusable function to decide whether we're "at the top" or not
function bodyIsAtTop() 
  return (
    document.body.scrollTop <= 40 &&
    document.documentElement.scrollTop <= 40
  );


class TopButton extends React.Component 

  constructor(props) 
    super(props);
    // *** Initial state
    this.state = 
      atTop: bodyIsAtTop()
    ;
    // *** No need for the following if you use an arrow function
    // this.handleScroll = this.handleScroll.bind(this);
  

  // *** Don't make this an arrow, make it a method
  componentDidMount() 
    window.addEventListener("scroll", this.handleScroll, true);
  ;

  // *** Need to unbind when unmounted
  componentWillUnmount = () => 
    window.removeEventListener("scroll", this.handleScroll, true);
  ;

  handleClick = () => 
    document.body.scrollTop = 0;
    document.documentElement.scrollTop = 0;
  ;

  handleScroll = () => 
    // *** Update state (possibly; if the flag isn't different, this doesn't do anything)
    this.setState(atTop: bodyIsAtTop());
  ;

  render() 
    // *** Get the flag from state, use it below in style
    const atTop = this.state;
    return (
      <div className="topbutton_container">
        <button
          style= display: atTop ? "none" : "block" 
          onClick=this.handleClick
          onScroll=this.handleScroll
          className="topbutton_button"
        >
          <img src=require("../assets/arrow-up.png") />
        </button>
      </div>
    );
  


export default TopButton;

我为handleScrollhandleClick 保留了你的箭头函数。有一个参数可以让它们成为方法并在构造函数中使用bind,但这主要是一种风格。 (嗯...风格,并且更容易模拟原型方法进行测试,这是使用原型方法和bind 的非风格原因。)

【讨论】:

感谢您的回答,我让它以不同的方式工作。我的方法是 1) 将 className 替换为 id,因此 document.getElementById("buttonid") buttonid is my ID for button 但是这种方法的问题是按钮会呈现第一次。 **2)**为了消除这种不一致,我在我的componentDidMount() 方法中设置了display = "none"。它工作正常。您认为这种方法有负面影响吗? @AbhinavAnshul - 它对库工作而不是使用它,这意味着您不能使用多个组件的副本(除非您将 id 作为道具)。不过,它可能有效

以上是关于如何在 ReactJS 的 onScroll 事件中操作 DOM?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不将 onscroll 处理程序附加到每个容器的情况下捕获页面上的所有滚动事件

JS - 滚动事件(onscroll)

在 Android ScrollView 中捕获 onScroll 事件

如何在Blazor服务器端处理窗口或主体滚动?

OnScroll 事件离子 2

ScrollView React Native 上的多个 onScroll 事件?