如何在 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;
我为handleScroll
和handleClick
保留了你的箭头函数。有一个参数可以让它们成为方法并在构造函数中使用bind
,但这主要是一种风格。 (嗯...风格,并且更容易模拟原型方法进行测试,这是使用原型方法和bind
的非风格原因。)
【讨论】:
感谢您的回答,我让它以不同的方式工作。我的方法是 1) 将 className 替换为 id,因此document.getElementById("buttonid")
buttonid is my ID for button 但是这种方法的问题是按钮会呈现第一次。 **2)**为了消除这种不一致,我在我的componentDidMount()
方法中设置了display = "none"
。它工作正常。您认为这种方法有负面影响吗?
@AbhinavAnshul - 它对库工作而不是使用它,这意味着您不能使用多个组件的副本(除非您将 id
作为道具)。不过,它可能有效。以上是关于如何在 ReactJS 的 onScroll 事件中操作 DOM?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不将 onscroll 处理程序附加到每个容器的情况下捕获页面上的所有滚动事件