Skip to content Skip to sidebar Skip to footer

Is There A Way To Override The Browser’s Default Scroll To Hash Behavior On Page Load?

I have a fixed positioned header that 'slides up to hide' when users scroll down similar to the one on Medium (example: https://medium.com/@sunknudsen/brute-forcing-your-very-own-v

Solution 1:

I would expect that your slides-up-to-hide header would slide up and hide in this case as part of how it's defined.

If not, you could respond to the DOMContentLoaded and window load events by looking at window.scrollY and triggering the slide-up-to-hide behavior at that point.

If your goal is for the browser not to scroll to the element, this works for me (iOS Safari, Chrome, and Brave):

window.addEventListener("DOMContentLoaded", function() {
    if (location.hash === "#foo") {
        setTimeout(function() {
            window.scrollTo(0,0);
        }, 0);
    }
});

It leaves the hash in the URL, but scrolls the window to top. I don't see a flash in my experiment, but your mileage may vary.


Solution 2:

Thanks to the precious feedback of @T.J.Crowder and to this question, I was able to put together a React component that overrides the browser’s default scroll to hash behavior on page load.

The behavior of this component is inspired by Medium. The page initially loads without scrolling to the element and, after a short delay (once everything has had time to load), it initiates the scroll. See https://medium.com/@sunknudsen/brute-forcing-your-very-own-vanity-onion-address-at-11-646mh-s-9a9c1fa93663#368a. I like how this delay helps contextualize the scroll.

The secret sauce is to quickly swap the id of the element to which the hash refers to (which disables the default scroll to hash) and to revert back to the real id once the DOM has had time to load.

export const scrollToWithOffset = (element: HTMLElement, offset:number, smooth?: boolean) => {
  let offsetTop = element.getBoundingClientRect().top + window.pageYOffset;
  let maxScrollTop = document.body.scrollHeight - document.body.clientHeight;
  let options: ScrollToOptions = {
    top: Math.min(offsetTop - offset, maxScrollTop),
    left: 0,
  };
  if (smooth === true) {
    options.behavior = 'smooth';
  }
  window.scroll(options);
};

interface ScrollToHashProps {}

interface ScrollToHashState {
  element?: HTMLElement;
}

export class ScrollToHash extends Component<ScrollToHashProps, ScrollToHashState> {
  constructor(props: ScrollToHashProps) {
    super(props);
    this.handleScroll = this.handleScroll.bind(this);
  }
  handleScroll() {
    setTimeout(() => {
      if (this.state.element) {
        this.state.element.id = this.state.element.id.replace('-sth', '');
        setTimeout(() => {
          if (this.state.element) {
            scrollToWithOffset(this.state.element, 0);
          }
        }, 1000);
      }
    }, 0);
  }
  componentDidMount() {
    if (window.location.hash !== '') {
      let element = document.getElementById(window.location.hash.replace('#', ''));
      if (element) {
        element.id += '-sth';
        this.setState({
          element: element
        });
        if (document.readyState === 'loading') {
          window.addEventListener('DOMContentLoaded', this.handleScroll);
        } else {
          this.handleScroll();
        }
      }
    }
  }
  componentWillUnmount() {
    window.removeEventListener('DOMContentLoaded', this.handleScroll);
  }
  render() {
    return null;
  }
}

Post a Comment for "Is There A Way To Override The Browser’s Default Scroll To Hash Behavior On Page Load?"