反应:单击锚标记以进行滚动时停止页面重新加载

Posted

技术标签:

【中文标题】反应:单击锚标记以进行滚动时停止页面重新加载【英文标题】: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 【参考方案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。

【讨论】:

这仍然会导致重新渲染

以上是关于反应:单击锚标记以进行滚动时停止页面重新加载的主要内容,如果未能解决你的问题,请参考以下文章

重新加载页面上有哈希值

如何在不重新加载 HTML 页面的情况下重定向锚标记链接?

如何在反应应用程序中重新加载每个页面时停止 firebase re-auth()?

反应:停止映射组件的重新渲染

如果通过单击锚点进行重定向,则停止重定向页面

Vue路由scrollBehavior滚动行为控制锚点