Element.scrollIntoView 是你的朋友。您可能会在您的刹车点(使用resize event)处找出窗口顶部的哪个元素,然后在该元素上调用element.scrollIntoView()。
您可以使用以下函数检测哪个元素最靠近顶部:
function closestToTop() {
const top = window.scrollY;
return [].reduce.call(
document.querySelectorAll('h1, h2, h3'),
(closest, el) => Math.abs(offsetTop(el) - top) <= Math.abs(offsetTop(closest) - top)
? el
: closest,
document.body
);
}
你在哪里得到offsetTop:
function offsetTop(el, offset = 0) {
if (el === document.body) {
return 0;
}
return offset + el.offsetTop + offsetTop(el.offsetParent);
}
我们将 self 限制为 <h1>、<h2> 和 <h3> 元素,以使计算更容易一些。
您要做的是在调整大小开始时弄清楚什么元素位于顶部,并且只有在滚动结束时它不在屏幕上时才滚动到它。所以我们需要申请某种throttle:
{
let throttling = false;
function throttledResize() {
if (throttling) {
return;
}
throttling = true;
window.dispatchEvent(new CustomEvent('resizeStart'));
requestAnimationFrame(() => {
window.dispatchEvent(new CustomEvent('resizeEnd'));
throttling = false;
});
};
window.addEventListener('resize', throttledResize);
}
现在我们有了'resizeStart' 和'resizeEnd' 事件。注意,我们把它放在 ECMAScript2015 块作用域中,所以我们不会用 throttling 和 throttleResize 变量污染全局作用域。现在我们可以在调整大小开始时关闭顶部的任何元素(再次使用块范围)。
{
let wasAtTop = null;
window.addEventListener('resizeEnd', () => {
if (!wasAtTop) {
return;
}
wasAtTop.scrollIntoView();
wasAtTop = null;
});
window.addEventListener('resizeStart', () => {
wasAtTop = closestToTop();
});
}
注意:这是高度未优化的。我们可以对此进行各种改进。就像油门的时间比下一个动画帧要长,除非元素实际上在视图之外window.scrollY < offsetTop(el) && offsetTop(el) < window.scrollY + window.height,否则永远不要滚动。