当窗口的大小发生变化时,有没有办法阻止内容滚动?
我正在制作一个包含长篇内容的自适应网站。当浏览器窗口调整大小时,读者放置在移位的内容中。这会导致糟糕的用户体验,因为他们必须再次在内容中找到自己的位置。
这方面的一个很好的例子是bootstrap doc站点。尝试调整docset底部附近的窗口大小,调整大小后你会看到完全不同的东西。 http://getbootstrap.com/components/#panels
1999年的Mozilla bug处理我面临#retrobug的问题 https://bugzilla.mozilla.org/show_bug.cgi?id=19261
答案 0 :(得分:1)
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);
}
我们将自己限制为<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
之外,否则永远不会滚动。
答案 1 :(得分:1)
很难将像素级的顶部精确地保持在调整窗口大小之前的位置(例如,在div的中间)。但作为一种解决方法,您可以使用一些div(例如节标题)作为“锚点”:每当用户滚动,检测并跟踪当前正在查看的锚点div时,并在调整大小后将页面固定回锚点。 / p>
更具体地说,以您提供的链接为例:http://getbootstrap.com/components/#panels
假设你想要所有<h1>
标签作为锚点(你可以改用h2,或者h1 + h2,取决于你想要的详细控制):
var anchorDivs = document.getElementsByTagName("h1");
作为一个开始的地方(没有测试每个角落的情况),我把这个函数放在一起来检测当前在视图中的第一个锚点div:
function getAnchorDiv(anchorDivs) {
var viewTop = document.body.scrollTop || document.documentElement.scrollTop; // IE
var viewBottom = viewTop + window.innerHeight;
var anchorDiv;
for (var i = 0; i < anchorDivs.length; i++) {
var anchorDiv = anchorDivs[i];
// gets the cumulative offset top of current div
var anchorDivTop = anchorDiv.offsetTop;
var tempDiv = anchorDiv.offsetParent;
while (tempDiv) {
anchorDivTop += tempDiv.offsetTop
tempDiv= tempDiv.offsetParent;
}
if (anchorDivTop >= viewTop && anchorDivTop < viewBottom) {
// this div is the first one in view
break;
} else if (anchorDivTop >= viewBottom) {
// page view doesn't contain any anchor div, so give the one that's just scrolled over (previous anchor div in the array)
anchorDiv = anchorDivs[(i-1) >= 0 ? (i-1) : 0];
break;
}
}
return anchorDiv;
}
当用户滚动时,更新锚点div:
var anchorDiv = anchorDivs[0];
document.onscroll = function() {
anchorDiv = getAnchorDiv(anchorDivs);
}
最后调整大小,滚动到那个锚点div,就像@RúnarBerg所说:
window.onresize = function() {
anchorDiv.scrollIntoView();
};
如果仅停留在url中的哈希位置,则可以在调整大小后重置window.location.href:
window.onresize = function() {
window.location.href = "http://getbootstrap.com/components/#panels";
};
答案 2 :(得分:0)
为了在调整大小时将内容保留在我们的窗口中,我们首先需要知道我们按百分比向下滚动了页面多远。
var percent;
window.addEventListener("scroll", getScrollPercent);
function getScrollPercent() {
percent = window.pageYOffset / document.body.scrollHeight;
}
首先,我将percent
设置为全局变量,因为我们将在另一个函数中暂时使用该值。
第二,此功能不是给我们滚动页面的百分比,而是从正文顶部到窗口顶部的百分比(您永远不会得到100%)。尽管这样做对我们有利,但是因为我们的下一个函数将调用window.scrollTo()
,该函数根据距页面顶部的偏移量滚动一定量。
window.addEventListener("resize", function(){
var Ypos = document.body.scrollHeight * percent;
window.scrollTo(0, Ypos);
});
我们使用先前计算的“百分比”来确定页面调整大小后滚动条应位于的位置,然后将所述值插入Y的window.scrollTo()
中,而将X值保留为0。
希望这对每个人都有效,如果我的措辞令人困惑/不愉快,我提前致歉。