使用带反应路由器的锚点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用带反应路由器的锚点相关的知识,希望对你有一定的参考价值。

如何使用react-router,并将链接导航到特定页面上的特定位置? (例如/home-page#section-three

细节:

我在我的React app中使用react-router

我有一个站点范围的导航栏,需要链接到页面的特定部分,如/home-page#section-three

因此,即使您在/blog上,单击此链接仍将加载主页,第三部分滚动到视图中。这正是标准<a href="/home-page#section-three>的工作原理。

注意:react-router的创建者没有给出明确的答案。他们说它正在进行中,并且同时使用其他人的答案。我会尽力保持这个问题的最新进展和可能的解决方案,直到出现主导的问题。

研究:


How to use normal anchor links with react-router

这个问题是从2015年(所以10年前的反应时间)。最受欢迎的答案是使用HistoryLocation而不是HashLocation。基本上这意味着将位置存储在窗口历史记录中,而不是存储在哈希片段中。

坏消息是......即使使用HistoryLocation(大多数教程和文档在2016年所说的内容),锚标签仍然不起作用。


https://github.com/ReactTraining/react-router/issues/394

ReactTraining上的一个线程,关于如何使用anchor链接与react-router。这是没有确认的答案。请注意,因为大多数建议的答案都已过时(例如使用<Link>中的“hash”道具)


答案

这是我找到的一个解决方案(2016年10月)。它是跨浏览器兼容的(在ie,ff,chrome,mobile safari,safari中测试)。

您可以为您的路由器提供onUpdate属性。只要路由更新,就会调用此方法。此解决方案使用onUpdate属性检查是否存在与哈希匹配的DOM元素,然后在路由转换完成后滚动到该元素。

您必须使用browserHistory而不是hashHistory。

答案是在这个thread中的“Rafrax”。

将此代码添加到您定义<Router>的位置:

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

const routes = (
  // your routes
);

function hashLinkScroll() {
  const { hash } = window.location;
  if (hash !== '') {
    // Push onto callback queue so it runs after the DOM is updated,
    // this is required when navigating from a different page so that
    // the element is rendered on the page before trying to getElementById.
    setTimeout(() => {
      const id = hash.replace('#', '');
      const element = document.getElementById(id);
      if (element) element.scrollIntoView();
    }, 0);
  }
}

render(
  <Router
    history={browserHistory}
    routes={routes}
    onUpdate={hashLinkScroll}
  />,
  document.getElementById('root')
)

如果您感觉懒惰并且不想复制该代码,则可以使用仅为您定义该功能的Anchorate。 https://github.com/adjohnson916/anchorate

另一答案

React Router Hash Link为我工作。易于安装和实施:

$ npm install --save react-router-hash-link

在component.js中将其导入为Link:

import { HashLink as Link } from 'react-router-hash-link';

而不是使用锚<a>,使用<Link>

<Link to="home-page#section-three">Section three</Link>

注意:我使用HashRouter而不是Router

另一答案

只是避免使用react-router进行本地滚动:

document.getElementById('myElementSomewhere').scrollIntoView() 
另一答案

https://stackoverflow.com/a/40280486/515585的问题有时候,如果该部分依赖于某些异步操作,则仍会呈现或加载具有id的元素。以下函数将尝试按id查找元素并导航到它并每100ms重试一次,直到最多重试50次:

scrollToLocation = () => {
  const { hash } = window.location;
  if (hash !== '') {
    let retries = 0;
    const id = hash.replace('#', '');
    const scroll = () => {
      retries += 0;
      if (retries > 50) return;
      const element = document.getElementById(id);
      if (element) {
        setTimeout(() => element.scrollIntoView(), 0);
      } else {
        setTimeout(scroll, 100);
      }
    };
    scroll();
  }
}
另一答案

另一种选择:react-scrollchor https://www.npmjs.com/package/react-scrollchor

react-scrollchor:一个React组件,用于滚动到具有平滑动画的#hash链接。 Scrollchor是Scroll和Anchor的混合体

注意:它不使用react-router

另一答案

这是一个简单的解决方案,不需要任何订阅或第三方包。应该与react-router@3及以上和react-router-dom合作。

工作示例:https://fglet.codesandbox.io/

来源(遗憾的是,目前在编辑器中不起作用):

Edit Simple React Anchor


ScrollHandler Hook Example

import { useEffect } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";

const ScrollHandler = ({ location, children }) => {
  useEffect(
    () => {
      const element = document.getElementById(location.hash.replace("#", ""));

      setTimeout(() => {
        window.scrollTo({
          behavior: element ? "smooth" : "auto",
          top: element ? element.offsetTop : 0
        });
      }, 100);
    }, [location]);
  );

  return children;
};

ScrollHandler.propTypes = {
  children: PropTypes.node.isRequired,
  location: PropTypes.shape({
    pathname: PropTypes.string,
    search: PropTypes.string,
    hash: PropTypes.string,
    state: PropTypes.any,
    key: PropTypes.string
  }).isRequired
};

export default withRouter(ScrollHandler);

ScrollHandler Class Example

import { PureComponent } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";

class ScrollHandler extends PureComponent {
  componentDidMount = () => this.handleScroll();

  componentDidUpdate = prevProps => {
    const { location: { pathname, hash } } = this.props;
    if (
      pathname !== prevProps.location.pathname ||
      hash !== prevProps.location.hash
    ) {
      this.handleScroll();
    }
  };

  handleScroll = () => {
    const { location: { hash } } = this.props;
    const element = document.getElementById(hash.replace("#", ""));

    setTimeout(() => {
      window.scrollTo({
        behavior: element ? "smooth" : "auto",
        top: element ? element.offsetTop : 0
      });
    }, 100);
  };

  render = () => this.props.children;
};

ScrollHandler.propTypes = {
  children: PropTypes.node.isRequired,
  location: PropTypes.shape({
    hash: PropTypes.string,
    key: PropTypes.string,
    pathname: PropTypes.string,
    search: PropTypes.string,
    state: PropTypes.any
  })
};

export default withRouter(ScrollHandler);
另一答案

我将Don P的解决方案(见上文)改编为react-router 4(2019年1月),因为onUpdate上没有<Router>道具了。

import React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, Route } from 'react-router';
import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory();

browserHistory.listen(location => {
    const { hash } = location;
    if (hash !== '') {
        // Push onto callback queue so it runs after the DOM is updated,
        // this is required when navigating from a different page so that
        // the element is rendered on the page before trying to getElementById.
        setTimeout(
            () => {
                const id = hash.replace('#', '');
                const element = document.getElementById(id);
                if (element) {
                    element.scrollIntoView();
                }
            },
            0
        );
    }
});

ReactDOM.render(
  <Router history={browserHistory}>
      // insert your routes here...
  />,
  document.getElementById('root')
)

以上是关于使用带反应路由器的锚点的主要内容,如果未能解决你的问题,请参考以下文章

URL中的锚点(fragment片段标识符)是什么?(hash mark(#))(HTML 页面内定位)(之前学html不是学了吗?忘啦?)(SEO 搜索引擎优化)

使用 nuxt-link 导航到不同页面上的锚点/哈希不起作用

Vue Router-更改滚动中的锚点

a标签中的锚点使用方法

如何将 HTML 页面滚动到给定的锚点

AR应用中的锚点检测问题