【问题标题】:reactjs sticky nav bar to scroll to a div using window.scrollTo doesn't workreactjs粘性导航栏使用window.scrollTo滚动到div不起作用
【发布时间】:2019-12-03 09:12:50
【问题描述】:

我有一个导航栏函数组件,它通过 onclick 回调将链接的名称传递给父组件,并进一步传递给主应用程序组件。应用程序组件有一个带有链接名称和关联引用的对象。应用程序组件中的 onclick 回调根据底层组件传递给它的链接名称从对象中获取 ref,并调用 window.scrollTo。 window.scrollTo 在您第一次单击链接时起作用,并且如果从粘性导航栏中单击另一个链接,则当页面滚动时,窗口不会再次滚动,而是返回到 (0,0) 并从那里单击相同链接有效。

//在App组件中回调。

manageContent(link){

   console.log(this.linksData[link].current)

    window.scrollTo({
        top: this.linksData[link].current.offsetTop,
        left: 0,
        behavior: 'smooth'
     })    
}

以上函数传递给Header组件

<Header links={data} onclick={this.manageContent} ref={this.headerRef}/>

并在标题中使用 NavLink 函数组件创建链接,其中 onClick 将返回链接名称。

我做错了什么,为什么当页面滚动到顶部而不是从页面中间或滚动位置时,scrollTo 在第二次点击时起作用。

我也尝试了其他滚动功能,但奇怪的是只有 scrollTo 滚动,而带有 scrollOptions、scrollToView、moveTo 等的滚动功能根本不起作用。

我在控制台打印出 offsetTop 并触发 window.scrollTo(0,"offsetTop print in console"),工作正常,没有问题。

这里是代码。

App.js

class App extends React.Component {

  constructor(props){
    super(props);
    this.manageContent = this.manageContent.bind(this)

    this.state={}
    this.sectionRef1 = React.createRef();
    this.sectionRef2 = React.createRef();
    this.sectionRef3 = React.createRef();
    this.sectionRef4 = React.createRef();
    this.sectionRef5 = React.createRef();    
    this.headerRef = React.createRef();    
    this.heroRef = React.createRef();
  }
  manageContent(key){
    console.log(key)
    this.setState({key:key});
  }

setActivePage = (key) => {
    let refList = [this.sectionRef1,this.sectionRef2,this.sectionRef3,this.sectionRef4,this.sectionRef5]
    console.log(key)
    console.log(refList[key].current)
    if (refList[key].current){
        window.scrollTo({behavior: "smooth",top: refList[key].current.offsetTop})

    }
}
componentDidUpdate(prevProps, prevState) {  
  console.log("comp updated")
  this.setActivePage(this.state.key)
}
/*
componentDidMount(){
  window.addEventListener('scroll', this.scrollListener)
}
componentWillUnmount() {
  window.removeEventListener('scroll', this.scrollListener)
}    
*/
  render(){     
    return (
      <div className="bp-container-full bp-typography" key="app">
        <Header links={data.links} onclick={this.manageContent} ref={this.headerRef}/>
        <main key="main">
            <HeroSection ref={this.heroRef}/>
            <div className="bp-main">
            <section key="home"className="page" ref={this.sectionRef1}>
              <Home/>
            </section>
            <section key="aboutme" className="page" ref={this.sectionRef2}>
              <AboutMe/>
            </section>
            <section key="sitedetails" className="page" ref={this.sectionRef4}>  
              <SiteDetails/>
            </section>
            <section key="contact" className="page" ref={this.sectionRef5}>  
              <ContactForm/>
            </section>
            </div>
        </main>
        <Footer/>  
      </div>
    );
  }

}
export default App;

Header.js

class Header extends React.Component {
    constructor(props) {
        super(props);
        console.log(props)
        this.linkRef = React.createRef()
        this.state = {isOpen:false};
        this.headerRef = React.createRef()
    }

    render() {      
        const navlink = data.links.map((link,key)=>{ 
            return(
            <a href="#" key={link} ref={this.linkRef}
                    className="nav-list-item bp-upper"
                    onClick={() => this.props.onclick(key)}>
                  {link}
            </a>
        )})
        return (
            <header key="header-key" className={classnames("bp-header","bp-header-fixed",
                            {"is-scrolled":this.state.scrolled})} ref={this.headerRef}>
                <button className={classnames("bp-mobile-menu",{"is-open":this.state.isOpen})} onClick={()=>{this.setState({isOpen:!this.state.isOpen})}}>
                  <i className={classnames("fas", {"fa-bars":!this.state.isOpen, "fa-times":this.state.isOpen})}></i>
                </button>                            
                <div className={classnames("nav", "nav-align-centre",{"is-open":this.state.isOpen})}>
                    <nav className="nav-list nav-primary">
                        {navlink}
                    </nav>
                </div>
            </header>
        )
    }
}
export default Header;

【问题讨论】:

  • 你传递给 mangeContent(link) 的值是多少
  • 传递给 manageContent 的值是链接的名称,例如“家”、“联系方式”。 App 组件中的对象类似于 {"home":homeRef,"contact":contactRef}
  • 为什么不只是this.linksData[link].current.scrollIntoView()
  • 也试过了,scrollIntoView() 根本不起作用,从控制台虽然也可以。

标签: reactjs react-ref


【解决方案1】:

在几乎多次编写整个应用程序之后终于发现了问题。

我的链接有一个 href,是的,我很傻,使用 href="#" 解决了这个问题,并且可能还解释了为什么它在两次点击而不是 1 次点击中起作用。

<a href="#" key={link} ref={this.linkRef}
                    className="nav-list-item bp-upper"
                    onClick={() => this.props.onclick(key)}>
                  {link}
            </a>

【讨论】:

    【解决方案2】:

    根据您提供的代码,尚不清楚可能有什么问题。我的假设是您分配参考或处理回调的方式有问题。

    这是一个工作沙箱,它将向您展示使其工作可能需要的所有代码:https://codesandbox.io/s/navbar-click-scroll-into-section-us8y7

    基本上与您的应用具有相同的布局。我们有一个带有链接的导航栏/标题,当点击一个时,我们会触发一个回调并找到相关联的ref。然后滚动到分配有该参考的部分。

    App.js

    import React from "react";
    import ReactDOM from "react-dom";
    import Header from "./Header";
    import HowItWorks from "./HowItWorks";
    import BrowserCatalogue from "./BrowserCatalogue";
    import Contact from "./Contact";
    import Woof from "./Woof";
    
    import "./styles.css";
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          selected: null
        };
      }
      //refs
      howItWorks = React.createRef();
      browserCatalogue = React.createRef();
      contact = React.createRef();
      woof = React.createRef();
    
      changeSelection = index => {
        this.setState({
          selected: index
        });
      };
    
      componentDidUpdate(prevProps, prevState) {
        this.scrollToSection(this.state.selected);
      }
    
      scrollToSection = index => {
        let refs = [
          this.howItWorks,
          this.browserCatalogue,
          this.contact,
          this.woof
        ];
    
        if (refs[index].current) {
          refs[index].current.scrollIntoView({
            behavior: "smooth",
            nearest: "block"
          });
        }
      };
    
      render() {
        return (
          <div className="App">
            <div>
              <Header changeSelection={this.changeSelection} />
            </div>
            <div ref={this.howItWorks}>
              <HowItWorks />
            </div>
            <div ref={this.browserCatalogue}>
              <BrowserCatalogue />
            </div>
            <div ref={this.contact}>
              <Contact />
            </div>
            <div ref={this.woof}>
              <Woof />
            </div>
          </div>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    Header.js

    import React from "react";
    
    const Header = props => {
      const { changeSelection } = props;
      return (
        <div
          style={{
            background: "green",
            height: "50px",
            width: "100%",
            position: "fixed",
            top: "0"
          }}
        >
          <span onClick={() => changeSelection(0)}>Working</span>{" "}
          <span onClick={() => changeSelection(1)}>Catalogue</span>{" "}
          <span onClick={() => changeSelection(2)}>Contact</span>{" "}
          <span onClick={() => changeSelection(3)}>Woof</span>
        </div>
      );
    };
    
    export default Header;
    

    【讨论】:

    • 我的代码和你的非常相似。我会清理我的代码,看看是否还有其他事情发生。非常感谢您的快速回复。
    • @bp82 不客气,它或多或少是相同的想法。让我知道您是否还想让我们看看。使用您发布的代码,很难推断出什么是错误的。
    • 现在贴出代码,还是不行。再次尝试使用 scrollToView() 并且它什么也没做,scrollTo 至少在 2 次点击后工作,第一个返回顶部,第二个返回页面。
    【解决方案3】:

    刚刚找到这个react ref with focus() doesn't work without setTimeout (my example),是的,这很有帮助。 setTimeOut 确实提供了一种修复,但这篇文章对我的代码可能有什么问题也很有意义。

    【讨论】:

    • 在上面的代码中,看起来你通过 props 将数据对象传递给 Headers。但未定义数据。
    • 为简洁起见,我没有发布导入,数据已导入且可用。从'./data/mydata'导入数据;
    • 嗨!您还需要集成此功能的帮助吗?
    • @ChristopherNgo 感谢您的帮助,刚刚发现什么对我不起作用。
    • 太棒了,它是什么 :)
    猜你喜欢
    • 2019-05-16
    • 2015-04-08
    • 2019-03-08
    • 2016-09-26
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 2021-07-28
    相关资源
    最近更新 更多