import React, { useEffect, useCallback, useRef } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; const elClassHidden = 'header--hidden'; const header = document.getElementById('header'); header.removeChild(document.getElementById('header_wrapper')); export default function Header({ children }) { let dHeight = useRef(0); let wHeight = useRef(0); let wScrollCurrent = useRef(0); let wScrollBefore = useRef(0); let wScrollDiff = useRef(0); useEffect(() => { window.addEventListener('scroll', () => (!window.requestAnimationFrame) ? throttle(250, updateHeader) : window.requestAnimationFrame(updateHeader), false); }, [updateHeader]); let throttle = (delay, fn) => { var last, deferTimer; return function () { var context = this, args = arguments, now = +new Date; if (last && now < last + delay) { clearTimeout(deferTimer); deferTimer = setTimeout( function () { last = now; fn.apply(context, args); }, delay); } else { last = now; fn.apply(context, args); } }; }; let updateHeader = useCallback(() => { dHeight.current = document.body.offsetHeight; wHeight.current = window.innerHeight; wScrollCurrent.current = window.pageYOffset; wScrollDiff.current = wScrollBefore.current - wScrollCurrent.current; if (wScrollCurrent.current <= 0) { // scrolled to the very top; element sticks to the top header.classList.remove(elClassHidden); } else if (wScrollDiff > 0 && header.classList.contains(elClassHidden)) { // scrolled up; element slides in header.classList.remove(elClassHidden); } else if (wScrollDiff.current < 0) { // scrolled down if (wScrollCurrent.current + wHeight.current >= dHeight.current && header.classList.contains(elClassHidden)) { // scrolled to the very bottom; element slides in header.classList.remove(elClassHidden); } else { // scrolled down; element slides out header.classList.add(elClassHidden); } } wScrollBefore.current = wScrollCurrent.current; }, []); return ReactDOM.createPortal(children, header); } Header.propTypes = { children: PropTypes.node };