【问题标题】:React: Stop page reload on clicking on anchor tag for scrolling反应:单击锚标记以进行滚动时停止页面重新加载
【发布时间】:2018-04-22 08:31:22
【问题描述】:

我目前正在使用 react 来创建一个维基百科风格的网站。出于数据输入的原因,我从数据库中获取整个 HTML,然后使用 dangerouslySetInnerHTML 设置其中的一部分,如下所示:

dangerouslySetInnerHTML={{ __html: this.props.section.text }}

现在页面的其他部分播放了视频,但是每次我单击<a href="#id> 样式标签(使用数据库设置)导航到同一页面的另一部分,整个页面刷新。

这会产生问题,因为视频也会重新加载并从头开始播放。

有没有什么方法可以在 React 中使用危险设置的锚标记滚动到页面的某些部分而无需重新加载整个页面?

编辑:使用以下版本:

“react”:“^16.0.0”,“react-router-dom”:“^4.2.2”

【问题讨论】:

  • 你使用的是哪个版本的 react-router?
  • @Chris 我正在使用 react-router@4.2.2

标签: javascript html reactjs react-router


【解决方案1】:

你可以做的是向你的组件添加一个 eventListener 来监听所有 href 属性值以 # 开头的锚标签。

componentDidMount() 方法中,选择href="#..." 所在的所有<a> 标签。您可以使用document.querySelectorAll("a[href^='#']") 执行此操作。这将返回一个包含所有匹配节点的数组。然后,您可以遍历它们并为每个附加一个 eventListener。

eventListener 将监听click 并运行一个阻止默认行为的函数(重定向到另一个页面),然后推送到您的路由器。 (删除下面的评论)

class MyApp extends React.Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    document.querySelectorAll("a[href^='#']").forEach(node => {
      node.addEventListener('click', e => {
        e.preventDefault();
        console.log(e.target.href);
        //this.props.history.push(e.target.href);
      });
    })
  }
 
  render() {
    const myMarkup = "<ul><li><a href='#'>Page link 1</a></li><li><a href='#test'>Page link 2</a></li><li><a href='https://google.com'>External link</a></li></ul>";
    return(
      <div id="container" dangerouslySetInnerHTML={{__html: myMarkup}}></div>
    );
  }
}
 
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

上面的 sn-p 假设您使用的是withRouter HOC。否则推送将不起作用。有关详细信息,请参阅我对 How to push to History in React Router v4? 的回复。

【讨论】:

  • 这仍然不能解决我的问题:(视频也是从数据库中获取的,每次单击锚标记时它仍然会重新启动,每次单击时我都可以在控制台中看到数据库获取以及(这最终是我试图阻止的)。无论如何不触发滚动上的 redux 操作?
【解决方案2】:

捕获默认事件处理程序并自己处理事件处理。你可以把它放在 componentDidMount 生命周期钩子中。还要在 componentWillUnmount 中删除这些处理程序

var aTags = document.querySelectorAll('a[href]'); //use apt selector for you section


     aTags.forEach(aTag =>aTag.addEventListener('click',function(e){
          e.preventDefault();
          e.nativeEvent.stopImmediatePropagation()
          e.stopPropagation()
          var href = e.target.href;
          history.pushState({},'',href);
        }));

【讨论】:

  • 使用 Jquery 选择器将事件侦听器附加到 DOM 或以任何方式修改它?这违背了使用 React 的全部目的。
  • @ShishirArora 更新版本又错了,因为 React 使用的是合成事件。
  • 所有合成事件都在文档对象中处理。我们可以使用这个
  • 感谢您的回答,虽然我分享了其他评论者的担忧,但我还必须补充一点,这并没有真正停止重新加载行为。还有其他方法吗?
  • @KehkashanFazal 立即尝试。更正语法
【解决方案3】:

要添加@Shishir Arora 所说的内容,而不是使用history.pushState({},'',href);,请使用document.getElementById(hash.replace('#', '')).scrollIntoView(); 之类的内容。

这是我利用 jQuery 的完整组件的代码:

import React, { useEffect } from 'react';
import $ from 'jquery';

const handleAnchorClick = e => {
  e.preventDefault();
  e.stopPropagation();

  const hash = e.currentTarget.hash;
  document.getElementById(hash.replace('#', '')).scrollIntoView();
  // Alternatively use jQuery for the above line: $(hash).get(0).scrollIntoView();
};

export default ({ response, url, search }) => {
  useEffect(() => {
    $('#article-content').on('click', 'a[href^="#"]', handleAnchorClick);

    return () => {
      $('#article-content').off('click', 'a[href^="#"]', handleAnchorClick);
    };
  }, [response]);

  return (
    <div>
      <div id='article-content' dangerouslySetInnerHTML={{__html: response}} />
    </div>
  );
};

jQuery 有助于捕获 currentTarget,由于标签中嵌套了一个元素,因此 @Shishir 的原始答案不会以相同的方式捕获该 currentTarget

【讨论】:

    【解决方案4】:

    由于您的问题带有“react-router”标签,我假设您在应用程序中使用它,我还假设您使用的是 v4.x 的最新版本。

    我过去解决这个问题的方法是使用React Router Hash link 包。正如他们的文档中所述,您可以这样使用它(将您的 &lt;a href="#id"&gt; 替换为 &lt;Link&gt;):

    // In YourComponent.js
    ...
    import { HashLink as Link } from 'react-router-hash-link';
    ...
    // Use it just like a RRv4 link (to can be a string or an object, see RRv4 api for details):
    <Link to="/some/path#with-hash-fragment">Link to Hash Fragment</Link>
    

    【讨论】:

    • 这仍然会导致重新渲染
    【解决方案5】:

    既然您使用的是 React Router v4,为什么不使用Link

    ...
    import { Link } from 'react-router-dom';
    ... 
    <Link to={{
      pathname: '/courses',
      search: '?sort=name',
      hash: '#the-hash',
      state: { fromDashboard: true }
    }}/>
    

    您可以找到文档here

    【讨论】:

    • 这仍然会导致重新渲染
    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 2013-10-16
    • 2023-03-20
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多