如何检测自动滚动到锚点?
How to detect automatic scrolling to anchors?
我有一个单页网站,example.com。有两个部分:页面顶部的 intro 和页面底部的 contact。如果我想让某人访问 contact 部分而不必滚动浏览 intro,我给他们这个 link:example.com/#contact。我在下面谈论这些访问。
浏览器自动向下滚动到 contact 部分,但它忽略了页面顶部的固定导航,因此 contact 部分滚动到导航后面,因此 contact 部分的顶部变得不可见。这是我想使用 JavaScript 更正的内容,方法是从滚动位置减去固定导航的高度。我们称此函数为 scrollCorrector
。问题是我不知道这种自动滚动发生的确切时间,所以 scrollCorrector
并不是每次都应该被调用。
什么时候调用 scrollCorrector
?当由于散列部分而发生自动滚动时。为什么不使用 onscroll
?因为这样我就无法区分自动滚动和用户滚动。为什么不对每个 <a href="example.com/#contact">
使用 onclick
?我会使用它,但如果用户通过浏览器的后退按钮导航怎么办?好的,我也会使用 onpopstate
。但是如果用户来自 example.com/#intro 通过手动将 URL 重写为 example.com/#contact?好的,我也会使用 onhashchange
。但是,如果用户已经在 example.com/#contact 上,点击地址栏,然后在没有任何修改的情况下按回车键怎么办? None 以上内容对您有帮助。
那我应该听什么事件呢?如果不存在这样的事件,scrollCorrector
怎么知道刚刚发生了自动滚动?
我看过了,是的,我可以看到你提到的使用 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>
滚动事件将会触发,所以你可以,
- 检查您的实际
location.hash
,如果为空我们不在乎
- debounce 确定不是鼠标滚轮触发的事件
- 获取实际的
document.querySelector(location.hash).getBoundingClientRect().top
,如果是 ===0
,则调用您的 scrollCorrector
。
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;
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;}
<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>
注意事项:
- 它在滚动事件和更正之间引入了 100 毫秒的超时,这是可见的。
- 它不是 100% 防弹的,用户只能触发一个事件(通过鼠标滚轮或键盘)并恰好落在正确的位置,因此会产生误报。但这种情况发生的可能性很小,因此这种行为可能是可以接受的。
我有一个单页网站,example.com。有两个部分:页面顶部的 intro 和页面底部的 contact。如果我想让某人访问 contact 部分而不必滚动浏览 intro,我给他们这个 link:example.com/#contact。我在下面谈论这些访问。
浏览器自动向下滚动到 contact 部分,但它忽略了页面顶部的固定导航,因此 contact 部分滚动到导航后面,因此 contact 部分的顶部变得不可见。这是我想使用 JavaScript 更正的内容,方法是从滚动位置减去固定导航的高度。我们称此函数为 scrollCorrector
。问题是我不知道这种自动滚动发生的确切时间,所以 scrollCorrector
并不是每次都应该被调用。
什么时候调用 scrollCorrector
?当由于散列部分而发生自动滚动时。为什么不使用 onscroll
?因为这样我就无法区分自动滚动和用户滚动。为什么不对每个 <a href="example.com/#contact">
使用 onclick
?我会使用它,但如果用户通过浏览器的后退按钮导航怎么办?好的,我也会使用 onpopstate
。但是如果用户来自 example.com/#intro 通过手动将 URL 重写为 example.com/#contact?好的,我也会使用 onhashchange
。但是,如果用户已经在 example.com/#contact 上,点击地址栏,然后在没有任何修改的情况下按回车键怎么办? None 以上内容对您有帮助。
那我应该听什么事件呢?如果不存在这样的事件,scrollCorrector
怎么知道刚刚发生了自动滚动?
我看过了,是的,我可以看到你提到的使用 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>
滚动事件将会触发,所以你可以,
- 检查您的实际
location.hash
,如果为空我们不在乎 - debounce 确定不是鼠标滚轮触发的事件
- 获取实际的
document.querySelector(location.hash).getBoundingClientRect().top
,如果是===0
,则调用您的scrollCorrector
。
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;
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;}
<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>
注意事项:
- 它在滚动事件和更正之间引入了 100 毫秒的超时,这是可见的。
- 它不是 100% 防弹的,用户只能触发一个事件(通过鼠标滚轮或键盘)并恰好落在正确的位置,因此会产生误报。但这种情况发生的可能性很小,因此这种行为可能是可以接受的。