UNPKG

@pharrellwang/react-router-hash-link

Version:

Hash link scroll functionality for React Router v4

73 lines (65 loc) 1.91 kB
import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; let hashFragment = ''; let observer = null; let asyncTimerId = null; function reset() { hashFragment = ''; if (observer !== null) observer.disconnect(); if (asyncTimerId !== null) { window.clearTimeout(asyncTimerId); asyncTimerId = null; } } function getElAndScroll() { const element = document.getElementById(hashFragment); if (element !== null) { element.scrollIntoView(); var scrolledY = window.scrollY; if(scrolledY){ // window.scroll(0, scrolledY - '[your header height in pixels]'); //E.g., window.scroll(0, scrolledY - 64); } reset(); return true; } return false; } function hashLinkScroll() { // Push onto callback queue so it runs after the DOM is updated window.setTimeout(() => { if (getElAndScroll() === false) { if (observer === null) { observer = new MutationObserver(getElAndScroll); } observer.observe(document, { attributes: true, childList: true, subtree: true }); // if the element doesn't show up in 10 seconds, stop checking asyncTimerId = window.setTimeout(() => { reset(); }, 10000); } }, 0); } export function HashLink(props) { function handleClick(e) { reset(); if (props.onClick) props.onClick(e); if (typeof props.to === 'string') { hashFragment = props.to.split('#').slice(1).join('#'); } else if (typeof props.to === 'object' && typeof props.to.hash === 'string') { hashFragment = props.to.hash.replace('#', ''); } if (hashFragment !== '') hashLinkScroll(); } return <Link {...props} onClick={handleClick}>{props.children}</Link>; } HashLink.propTypes = { onClick: PropTypes.func, children: PropTypes.node, to: PropTypes.oneOfType([ PropTypes.string, PropTypes.object, ]), };