有没有一种方法可以覆盖浏览器的默认滚动以在页面加载时显示哈希行为?

时间:2020-01-14 14:50:05

标签: javascript html

我有一个固定位置的标题,当用户向下滚动时(类似于https://medium.com/@sunknudsen/brute-forcing-your-very-own-vanity-onion-address-at-11-646mh-s-9a9c1fa93663#368a),该标题会“向下滑动以隐藏”。

问题在iOS上,当浏览器滚动到页面加载时哈希(例如,在Medium上为#368a)时,某些内容(例如在Medium上为How to get your very own vanity onion address?)出现在标题下方。

我希望处理(覆盖)默认的滚动到哈希逻辑(类似于event.preventDefault()之类的东西),以便更好地控制滚动位置,并考虑标头的高度。

我能够使用以下逻辑在React上处理页内导航。

const scrollWithOffset = (element: HTMLElement, offset:number) => {
  let offsetTop = element.getBoundingClientRect().top;
  const elementPosition = offsetTop - offset - 20;
  window.scroll({
    top: elementPosition,
    left: 0,
    behavior: 'smooth'
  });
};

<HashLink
  to={props.href}
  scroll={el => scrollWithOffset(el, 104)}
  smooth
>{props.children}</HashLink>

很乐意将相同的逻辑应用于默认滚动,以在页面加载时实现哈希行为。

2 个答案:

答案 0 :(得分:2)

在这种情况下,我希望您的幻灯片向上隐藏标头会向上滑动并隐藏,作为定义方式的一部分。

如果没有,您可以通过查看DOMContentLoaded并在那一刻触发向上滑动隐藏行为来响应windowload window.scrollY事件

如果您的目标是浏览器不要滚动到该元素,那么这对我有效(iOS Safari,Chrome和Brave):

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

它将哈希保留在URL中,但将窗口滚动到顶部。我的实验中没有看到闪光灯,但是您的行驶里程可能会有所不同。

答案 1 :(得分:0)

由于@ T.J.Crowder和this question的宝贵反馈,我能够将一个React组件放在一起,该组件可以覆盖浏览器默认的滚动到页面加载时的哈希行为。

此组件的行为受Medium启发。页面最初加载时没有滚动到该元素,并且经过短暂延迟(一旦所有内容都有时间加载),它就会启动滚动。参见https://medium.com/@sunknudsen/brute-forcing-your-very-own-vanity-onion-address-at-11-646mh-s-9a9c1fa93663#368a。我喜欢这种延迟如何帮助使滚动内容更加生动。

秘密之处在于快速交换哈希所引用的元素的id(这将禁用默认滚动滚动到哈希),并在DOM具有后恢复为真实的id有时间加载。

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;
  }
}