如何检测自动滚动到锚点?

时间:2015-10-02 23:18:17

标签: javascript javascript-events anchor hashchange

我有一个单页网站 example.com 。有两个部分:页面顶部的简介,以及页面底部的联系人。如果我希望有人访问联系人部分而无需滚动简介,我会给他们这个链接: example.com/#contact 。我正在谈论下面的这些访问。

浏览器会自动向下滚动到联系人部分,但它会忽略页面顶部的固定导航,因此联系人部分会在导航后面滚动联系人部分的顶部变为不可见。这是我想用JavaScript修正,通过从滚动位置减去固定导航的高度。我们将此函数称为scrollCorrector。问题是我不确切知道何时发生这种自动滚动,因此scrollCorrector每次都不会被调用。

何时应该调用scrollCorrector?当由于散列部分而发生自动滚动时。为什么不使用onscroll?因为这样我无法区分用户滚动的自动滚动。为什么不在每个onclick上使用<a href="example.com/#contact">?我将使用它,但如果用户通过浏览器的后退按钮导航会怎么样?好的,我也会使用onpopstate。但是,如果用户通过手动将URL重写为 example.com/#contact 来来自 example.com/#intro ,该怎么办?好的,我也会使用onhashchange。但是,如果用户已经在 example.com/#contact 上,点击地址栏并按下Enter而不做任何修改,该怎么办?以上都没有帮助。

那我应该听什么事?如果这样的事件不存在,scrollCorrector怎么知道自动滚动刚刚发生?

2 个答案:

答案 0 :(得分:2)

滚动事件将会触发,所以你可以,

  • 检查您的实际location.hash,如果为空,我们不在乎
  • debounce事件确定它不是触发它的鼠标滚轮
  • 获取实际document.querySelector(location.hash).getBoundingClientRect().top,如果是===0,请拨打scrollCorrector

&#13;
&#13;
var AnchorScroller = function() {
  // a variable to keep track of last scroll event
  var last = -100, // we set it to -100 for the first call (on page load) be understood as an anchor call
    // a variable to keep our debounce timeout so that we can cancel it further
    timeout;

  this.debounce = function(e) {
    // first check if we've got a hash set, then if the last call to scroll was made more than 100ms ago
    if (location.hash !== '' && performance.now() - last > 100)
    // if so, set a timeout to be sure there is no other scroll coming
      timeout = setTimeout(shouldFire, 100);
    // that's not an anchor scroll, stop it right now !	
    else clearTimeout(timeout);
    // set the new timestamp
    last = performance.now();
  }

  function shouldFire() {
    // a pointer to our anchored element
    var el = document.querySelector(window.location.hash);
    // if non-null (an other usage of the location.hash) and that it is at top of our viewport
    if (el && el.getBoundingClientRect().top === 0) {
      // it is an anchor scroll
      window.scrollTo(0, window.pageYOffset - 64);
    }
  }
};
window.onscroll = new AnchorScroller().debounce;
&#13;
body {
  margin: 64px 0 0 0;
}
nav {
  background: blue;
  opacity: 0.7;
  position: fixed;
  top: 0;
  width: 100%;
}
a {
  color: #fff;
  float: left;
  font-size: 30px;
  height: 64px;
  line-height: 64px;
  text-align: center;
  text-decoration: none;
  width: 50%;
}
div {
  background: grey;
  border-top: 5px #0f0 dashed;
  font-size: 30px;
  padding: 25vh 0;
  text-align: center;
}
#intro,#contact {  background: red;}
&#13;
<nav>
  <a href="#intro">Intro</a>
  <a href="#contact">Contact</a>
</nav>
<div id="intro">  Intro </div>
<div> Lorem </div>
<div id="contact">  Contact </div>
<div> Ipsum </div>
&#13;
&#13;
&#13;

警告:
- 它在滚动事件和校正之间引入了100ms超时,这是可见的 - 它不是100%防弹,用户只能触发一个事件(通过鼠标滚轮或键盘)并完全落在正确的位置,因此产生误报。但是,这种情况发生的可能性很小,以至于这种行为可能是可以接受的。

答案 1 :(得分:1)

我看过了,是的,我可以看到你使用window.onhashchange提到的限制。

我明白你想要什么,但我不认为存在这样的事情。

这是我提出的最好的(完全放弃hashchange):

<html>
<head>
<script>
"use strict";
(function () {
    window.myFunc = function(href) {
        window.alert("Link clicked, hash is: " + href);
    };
    window.alert("Page just reloaded, hash is: " + window.location.hash);
})();
</script>
</head>
<body>
<a href="#a" onclick="myFunc(this.hash)">a</a><br />
<a href="#b" onclick="myFunc(this.hash)">b</a>
<h1 id="a">a</a>
<h1 id="b">b</a>
</body>
</html>