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

时间:2016-09-26 01:43:36

标签: javascript html css

当窗口的大小发生变化时,有没有办法阻止内容滚动?

我正在制作一个包含长篇内容的自适应网站。当浏览器窗口调整大小时,读者放置在移位的内容中。这会导致糟糕的用户体验,因为他们必须再次在内容中找到自己的位置。

这方面的一个很好的例子是bootstrap doc站点。尝试调整docset底部附近的窗口大小,调整大小后你会看到完全不同的东西。 http://getbootstrap.com/components/#panels

1999年的Mozilla bug处理我面临#retrobug的问题 https://bugzilla.mozilla.org/show_bug.cgi?id=19261

3 个答案:

答案 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块范围内,因此我们不会使用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之外,否则永远不会滚动。

答案 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。

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