基于滚动React JS切换类

时间:2017-06-18 06:33:01

标签: javascript twitter-bootstrap reactjs events javascript-events

我使用bootstrap 4导航栏,想要在ig 400px向下滚动后更改背景颜色。我正在查看反应文档,发现了一个onScroll,但无法找到相关信息。到目前为止我已经

我不知道我是否正在使用正确的事件监听器或如何设置身高等。

我并没有真正设置内联样式

  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 (
   <div>

       <Navbar inverse toggleable className={this.state.scrollBackground} 
                                  onScroll={this.handleScroll}>
        ...
      </Navbar>

    </div>
   );
  }
}

export default App;

9 个答案:

答案 0 :(得分:9)

添加滚动侦听器的一种方法是使用componentDidMount()生命周期方法。以下示例应该给你一个想法:

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 (
      <div style={{ height: '200vh' }}>
        <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}!</h2>
      </div>
    );
  }
} 

render(<App />, document.getElementById('root'));

当您的“滚动”位置为100及以上时,这会将文本从“向下滚动”更改为“向上滚动”。

编辑:应该避免在每个滚动上更新状态的过度杀伤。仅在布尔值更改时更新它。

答案 1 :(得分:4)

 const [scroll, setScroll] = useState(false);

 useEffect(() => {
   window.addEventListener("scroll", () => {
     setScroll(window.scrollY > specify_height_you_want_to_change_after_here);
   });
 }, []); 

然后您可以根据滚动条更改班级或其他任何内容。

<nav className={scroll ? "bg-black" : "bg-white"}>...</nav>

答案 2 :(得分:2)

这是更好的

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 (
      <div style={{ height: '200vh' }}>
        <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}!</h2>
      </div>
    );
  }
} 

render(<App />, document.getElementById('root'));

答案 3 :(得分:2)

对于那些在2019年阅读此问题的人,我采取了@glennreyes的答案,并使用了 React Hooks

  const [scroll, setScroll] = useState(0)

  useEffect(() => {
    document.addEventListener("scroll", () => {
      const scrollCheck = window.scrollY < 100
      if (scrollCheck !== scroll) {
        setScroll(scrollCheck)
      }
    })
  })

请记住, useState 具有两个元素的数组,首先是状态对象,其次是对其进行更新的功能

基本上, useEffect 帮助我们替换了 componentDidmount ,当前编写的函数不会进行任何清理,因为在这种情况下是不必要的。

如果您发现有必要进行清理,则只需在 useEffect 内部返回一个函数。

您可以全面阅读here

答案 4 :(得分:0)

我已将@PouyaAtaei的用例答案做了些修改。

 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

}
};

呼叫钩子:

Const component = () => {

// calling our custom hook and optional distance agument.
const scroll = useHasScrolled(250)

}

答案 5 :(得分:0)

这是/我对滚动显示和隐藏随机页面元素的挂钩方法的另一种看法。

我受到了Dan Abramov's post here的启发。

您可以在此CodeSandbox demo中查看完整的工作示例。

以下是useScroll自定义钩子的代码:

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);
  });
};

这个钩子将像这样被消耗:

  useScroll(direction => {
    setScrollDirection(direction);
  });

使用此自定义钩子的完整组件:

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 (
    <div>
      {/* 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 />
    </div>
  );
}

const rootElement = document.getElementById("root");

ReactDOM.render(<Page />, rootElement);

最后是CustomElement的代码:

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
    </div>
  );
};

答案 6 :(得分:0)

这是两个钩子-一个用于方向(上/下/无),另一个用于实际位置

像这样使用:

useScrollPosition(position => {
    console.log(position)
  })

useScrollDirection(direction => {
    console.log(direction)
  })

这是钩子:

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)
  })
}

答案 7 :(得分:0)

如何修复:

警告:无法对已卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。 菜单新闻

const [scroll, setScroll] = useState(false);

 useEffect(() => {
   window.addEventListener("scroll", () => {
     setScroll(window.scrollY > specify_height_you_want_to_change_after_here);
   });
 }, []); 

答案 8 :(得分:0)

没有滚动事件监听器的方法

import { useEffect, useState } from "react";

interface Props {
  elementId: string;
  position: string;
}

const useCheckScrollPosition = ({ elementId, position }: Props) => {
  const [isOverScrollPosition, setIsOverScrollPosition] = useState<boolean>(false);

  useEffect(() => {
    if (
      "IntersectionObserver" in window &&
      "IntersectionObserverEntry" in window &&
      "intersectionRatio" in window.IntersectionObserverEntry.prototype
    ) {
      const observer = new IntersectionObserver((entries) => {
        setIsOverScrollPosition(entries[0].boundingClientRect.y < 0);
      });
      const flagElement = document.createElement("div");
      flagElement.id = elementId;
      flagElement.className = "scroll-flag";
      flagElement.style.top = position;

      const container = document.getElementById("__next"); // React div id
      const oldFlagElement = document.getElementById(elementId);

      if (!oldFlagElement) container?.appendChild(flagElement);

      const elementToObserve = oldFlagElement || flagElement;
      observer.observe(elementToObserve);
    }
  }, [elementId, position]);

  return isOverScrollPosition;
};

export default useCheckScrollPosition;

然后你可以像这样使用它:

const isOverScrollPosition = useCheckScrollPosition({
    elementId: "sticky-header",
    position: "10px",
  });

isOverScrollPosition 是一个布尔值,如果您滚动到位置提供的值 (10 像素),则为 true,如果滚动到其下方,则为 false。

这种方法会在 react root 中添加一个标志 div。

参考:https://css-tricks.com/styling-based-on-scroll-position/