首页 > 解决方案 > 如何在 ReactJS 的 onScroll 事件中操作 DOM?

问题描述

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;

标签: javascripthtmlreactjsdom

解决方案


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

  • 查看这个问题的答案;基本上,getElementsByClassName返回一个HTMLCollection,而不是单个元素,但是您注释掉的代码将其视为单个元素。

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

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

  1. 将按钮状态(无论它是否应该被阻止)在组件中保持为状态;

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

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

其他一些注意事项:

  • 卸载组件时,您还需要删除处理程序

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

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

这些方面的东西,请参阅***评论

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;

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


推荐阅读