简体   繁体   English

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

[英]How can i manipulate DOM inside the onScroll event in ReactJS?

I am trying to creating an arrow-up button, which will be set to display: "none" if I am at a top of my page, and when I scroll down further, I want to set it to display: "block".我正在尝试创建一个向上箭头按钮,如果我在页面顶部,它将设置为display: "none" ,当我进一步向下滚动时,我想将其设置为显示:“块”。 How can i achieve this?我怎样才能做到这一点? I am trying to manipulate the DOM inside my handleScroll function but it doesn't work.我试图在我的 handleScroll 函数中操作 DOM,但它不起作用。

My TopButton.js component我的 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;

There are at least two reasons it didn't work:它不起作用至少有两个原因:

  • See this question's answers ;查看此问题的答案 basically, getElementsByClassName returns an HTMLCollection , not a single element, but your commented-out code was treating it as though it were a single element.基本上, getElementsByClassName返回一个HTMLCollection ,而不是单个元素,但是您注释掉的代码将其视为单个元素。

  • If your component was ever re-rendered, it would be rendered in its default state, not the updated state you changed via the DOM如果您的组件曾经被重新渲染,它将以默认状态呈现,而不是您通过 DOM 更改的更新状态

But that's not how you'd do it with React.但这不是你用 React 做的事情。 Instead, you'd:相反,你会:

  1. have the button state (whether it should be block or not) held as state in your component;将按钮状态(无论是否应为块状态)作为组件中的状态保存;

  2. use that state when rendering the topbutton_button , setting its style or class accordingly;在渲染topbutton_button时使用该状态,相应地设置其样式或类; and

  3. update that state in your handleScroll handlerhandleScroll处理程序中更新该状态

A couple of others notes:其他一些注意事项:

  • You also need to remove your handler when the component is unmounting您还需要在卸载组件时删除处理程序

  • You shouldn't use arrow functions for component lifecycle functions您不应该将箭头函数用于组件生命周期函数

  • You don't need to use bind on an arrow function ( handleScroll for instance).您不需要在箭头函数(例如handleScroll )上使用bind Either make it an arrow function or use bind in the constructor to bind it.要么使它成为箭头函数,要么在构造函数中使用bind来绑定它。

Something along these lines, see the *** comments沿着这些路线,请参阅***评论

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;

There I've kept your arrow functions for handleScroll and handleClick .我在那里保留了handleScrollhandleClick的箭头功能。 There's an argument for making them methods and using bind in the constructor instead, but it's mostly a style thing.有一种说法是让它们成为方法并在构造函数中使用bind ,但这主要是一种风格。 (Well...style and it's easier to mock prototype methods for testing, which is a non-style reason for using prototype methods and bind .) (好吧......样式并且模拟原型方法进行测试更容易,这是使用原型方法和bind的非样式原因。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM