Toggle Class based on scroll React JS
我正在使用bootstrap 4导航栏,并想在ig 400px向下滚动后更改背景颜色。 我在查看react文档时发现了onScroll,但找不到太多信息。 到目前为止,我有...
我不知道我是否使用了正确的事件侦听器或如何设置高度等。
而且我并没有真正设置内联样式...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import React, { Component } from 'react'; class App extends Component { constructor(props) { super(props); this.state = { scrollBackground: 'nav-bg' }; this.handleScroll = this.handleScroll.bind(this); } handleScroll(){ this.setState ({ scrollBackground: !this.state.scrollBackground }) } render() { const scrollBg = this.scrollBackground ? 'nav-bg scrolling' : 'nav-bg'; return ( <Navbar inverse toggleable className={this.state.scrollBackground} onScroll={this.handleScroll}> ... </Navbar> ); } } export default App; |
添加滚动侦听器的一种方法是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { state = { isTop: true, }; componentDidMount() { document.addEventListener('scroll', () => { const isTop = window.scrollY < 100; if (isTop !== this.state.isTop) { this.setState({ isTop }) } }); } render() { return ( <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}! ); } } render(<App />, document.getElementById('root')); |
当您的scrollY位置在100或更高位置时,这会将文本从"向下滚动"更改为"向上滚动"。
编辑:应避免过分更新每个滚动条上的状态。仅在布尔值更改时更新它。
对于那些在2019年阅读此问题的人,我采取了@glennreyes的答案并使用React Hooks将其重写:
1 2 3 4 5 6 7 8 9 10 | const [scroll, setScroll] = useState(0) useEffect(() => { document.addEventListener("scroll", () => { const scrollCheck = window.scrollY < 100 if (scrollCheck !== scroll) { setScroll(scrollCheck) } }) }) |
请记住,useState具有两个元素的数组,首先是状态对象,其次是更新它的函数。
总的来说,useEffect可以帮助我们替换componentDidmount,当前编写的函数不会进行任何清理,因为在这种情况下没有必要。
如果发现清理非常重要,则只需在useEffect内部返回一个函数。
您可以在这里全面阅读。
更新:
如果你们想使其模块化,甚至可以进行清理,则可以执行以下操作:
创建一个如下所示的自定义钩子;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import { useState, useEffect } from"react" export const useScrollHandler = () => { // setting initial value to true const [scroll, setScroll] = useState(1) // running on mount useEffect(() => { const onScroll = () => { const scrollCheck = window.scrollY < 10 if (scrollCheck !== scroll) { setScroll(scrollCheck) } } // setting the event handler from web API document.addEventListener("scroll", onScroll) // cleaning up from the web API return () => { document.removeEventListener("scroll", onScroll) } }, [scroll, setScroll]) return scroll } |
在您认为合适的任何组件内调用它:
1 2 3 4 5 6 7 8 | const component = () => { // calling our custom hook const scroll = useScrollHandler() ....... rest of your code } |
更好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { constructor(props) { super(props); this.state = { isTop: true }; this.onScroll = this.onScroll.bind(this); } componentDidMount() { document.addEventListener('scroll', () => { const isTop = window.scrollY < 100; if (isTop !== this.state.isTop) { this.onScroll(isTop); } }); } onScroll(isTop) { this.setState({ isTop }); } render() { return ( <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}! ); } } render(<App />, document.getElementById('root')); |
1 2 3 4 5 6 7 | const [scroll, setScroll] = useState(false); useEffect(() => { window.addEventListener("scroll", () => { setScroll(window.scrollY > specify_height_you_want_to_change_after_here); }); }, []); |
然后,您可以根据滚动条更改班级或其他任何内容。
1 | <nav className={scroll ?"bg-black" :"bg-white"}>...</nav> |
这是用于滚动显示和隐藏随机页面元素的另一种方法。
我受到了很多启发:Dan Abramov在这里的帖子。
您可以在此CodeSandbox演示中查看完整的工作示例。
以下是useScroll自定义钩子的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import React, { useState, useEffect } from"react"; export const useScroll = callback => { const [scrollDirection, setScrollDirection] = useState(true); const handleScroll = () => { const direction = (() => { // if scroll is at top or at bottom return null, // so that it would be possible to catch and enforce a special behaviour in such a case. if ( window.pageYOffset === 0 || window.innerHeight + Math.ceil(window.pageYOffset) >= document.body.offsetHeight ) return null; // otherwise return the direction of the scroll return scrollDirection < window.pageYOffset ?"down" :"up"; })(); callback(direction); setScrollDirection(window.pageYOffset); }; // adding and cleanning up de event listener useEffect(() => { window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }); }; |
这个钩子将像这样被消耗:
1 2 3 | useScroll(direction => { setScrollDirection(direction); }); |
使用此自定义钩子的完整组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import React, { useState } from"react"; import ReactDOM from"react-dom"; import CustomElement, { useScroll } from"./element"; import Scrollable from"./scrollable"; function Page() { const [scrollDirection, setScrollDirection] = useState(null); useScroll(direction => { setScrollDirection(direction); }); return ( {/* a custom element that implements some scroll direction behaviour */} {/*"./element" exports useScroll hook and <CustomElement> */} <CustomElement scrollDirection={scrollDirection} /> {/* just a lorem ipsum long text */} <Scrollable /> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<Page />, rootElement); |
最后是CustomElement的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import React, { useState, useEffect } from"react"; export default props => { const [elementVisible, setElementVisible] = useState(true); const { scrollDirection } = props; // when scroll direction changes element visibility adapts, but can do anything we want it to do // U can use ScrollDirection and implement some page shake effect while scrolling useEffect(() => { setElementVisible( scrollDirection ==="down" ? false : scrollDirection ==="up" ? true : true ); }, [scrollDirection]); return ( <div style={{ background:"#ff0", padding:"20px", position:"fixed", width:"100%", display: `${elementVisible ?"inherit" :"none"}` }} > element ); }; |
这是两个钩子-一个用于方向(向上/向下/无),另一个用于实际位置
像这样使用:
1 2 3 4 5 6 7 | useScrollPosition(position => { console.log(position) }) useScrollDirection(direction => { console.log(direction) }) |
这些是钩子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import { useState, useEffect } from"react" export const SCROLL_DIRECTION_DOWN ="SCROLL_DIRECTION_DOWN" export const SCROLL_DIRECTION_UP ="SCROLL_DIRECTION_UP" export const SCROLL_DIRECTION_NONE ="SCROLL_DIRECTION_NONE" export const useScrollDirection = callback => { const [lastYPosition, setLastYPosition] = useState(window.pageYOffset) const [timer, setTimer] = useState(null) const handleScroll = () => { if (timer !== null) { clearTimeout(timer) } setTimer( setTimeout(function () { callback(SCROLL_DIRECTION_NONE) }, 150) ) if (window.pageYOffset === lastYPosition) return SCROLL_DIRECTION_NONE const direction = (() => { return lastYPosition < window.pageYOffset ? SCROLL_DIRECTION_DOWN : SCROLL_DIRECTION_UP })() callback(direction) setLastYPosition(window.pageYOffset) } useEffect(() => { window.addEventListener("scroll", handleScroll) return () => window.removeEventListener("scroll", handleScroll) }) } export const useScrollPosition = callback => { const handleScroll = () => { callback(window.pageYOffset) } useEffect(() => { window.addEventListener("scroll", handleScroll) return () => window.removeEventListener("scroll", handleScroll) }) } |
我已将@PouyaAtaei答案更改为我的用例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import { useState, useEffect } from"react" // Added distance parameter to determine how much // from the top tell return value is updated. // The name of the hook better reflects intended use. export const useHasScrolled = (distance = 10) => { // setting initial value to false const [scroll, setScroll] = useState(false) // running on mount useEffect(() => { const onScroll = () => { // Logic is false tell user reaches threshold, then true after. const scrollCheck = window.scrollY >= distance; if (scrollCheck !== scroll) { setScroll(scrollCheck) } } // setting the event handler from web API document.addEventListener("scroll", onScroll) // cleaning up from the web API return () => { document.removeEventListener("scroll", onScroll) } }, [scroll, setScroll]) return scroll } }; |
调用钩子:
1 2 3 4 5 6 | Const component = () => { // calling our custom hook and optional distance agument. const scroll = useHasScrolled(250) } |