调整浏览器窗口大小时,请保留内容中的位置

Keep place in content when resizing the browser window

本文关键字:位置 保留 窗口 浏览器 小时 调整      更新时间:2023-09-26

当窗口大小发生变化时,是否有方法阻止内容滚动?

我正在开发一个具有长格式内容的响应式网站。当调整浏览器窗口的大小时,阅读器在内容中的位置会发生移动。这导致了糟糕的用户体验,因为他们必须再次在内容中找到自己的位置。

bootstrap文档站点就是一个很好的例子。试着在文档集底部附近调整窗口大小,调整大小后你会看到完全不同的东西。http://getbootstrap.com/components/#panels

Mozilla漏洞来自1999年处理我面临的一个问题#retrobughttps://bugzilla.mozilla.org/show_bug.cgi?id=19261

Element.scrollIntoView是您的好友。您可以计算出哪个元素在您的制动点(使用resize事件(偏离路线的窗口顶部,并对该元素调用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>元素,以使计算更容易。

你想做的是在调整大小开始时弄清楚什么元素在顶部,并且只有在滚动结束时它在屏幕外时才滚动到它。因此,我们需要应用某种油门:

{
  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块作用域中,这样我们就不会用throttlingthrottleResize变量污染全局作用域。现在,我们可以关闭开始调整大小时位于顶部的任何元素(再次使用块范围(。

{
  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之外,否则永远不要滚动。

很难将顶部保持在像素级,精确到窗口调整大小之前的位置(例如,在div的中间(。但作为一种变通方法,您可以使用一些div(例如小节标题(作为"锚点":每当用户滚动时,检测并跟踪当前视图中的锚点div,并在调整大小后将页面固定回锚点。

更具体地说,以您提供的链接为例: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); 
}

最后,在调整大小时,滚动到@Rúnar Berg建议的锚点div:

window.onresize = function() {
    anchorDiv.scrollIntoView();
};

如果只是停留在url中的散列位置,您可以在调整大小后重置window.location.href:

window.onresize = function() {
    window.location.href = "http://getbootstrap.com/components/#panels";
};

为了在调整大小时保持窗口中的内容,我们首先需要知道我们通过百分比向下滚动了页面的距离。

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。

希望这对每个人都有效,如果我的措辞令人困惑,我提前道歉。