【问题标题】:Keep place in content when resizing the browser window调整浏览器窗口大小时保留内容
【发布时间】:2020-04-14 08:37:20
【问题描述】:

有没有办法在窗口大小改变时阻止内容滚动?

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

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

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

【问题讨论】:

    标签: javascript html css


    【解决方案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);
    }
    

    我们将 self 限制为 &lt;h1&gt;&lt;h2&gt;&lt;h3&gt; 元素,以使计算更容易一些。

    您要做的是在调整大小开始时弄清楚什么元素位于顶部,并且只有在滚动结束时它不在屏幕上时才滚动到它。所以我们需要申请某种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 &lt; offsetTop(el) &amp;&amp; offsetTop(el) &lt; window.scrollY + window.height,否则永远不要滚动。

    【讨论】:

    • 谢谢。这就是我想知道的。我如何“找出窗口顶部的元素”?
    • 我编辑了答案并进一步尝试实现这一目标。
    【解决方案2】:

    在调整窗口大小之前(例如,在 div 的中间),很难将顶部准确地保持在像素级别。但作为一种解决方法,您可以使用一些 div(例如部分标题)作为“锚点”:每当用户滚动时,检测并跟踪当前查看的锚点 div,并在调整大小后将页面固定回锚点。

    更具体地说,以您提供的链接为例:http://getbootstrap.com/components/#panels

    假设您希望所有 &lt;h1&gt; 标记作为锚点(您可以使用 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中的hash位置,可以在resize后重置window.location.href:

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

    【讨论】:

    • ty @js。当您只想让人们留在锚点时,这很有效。如果你在页面的不同点怎么办?我想您必须记录您在页面中的位置并参考该点。有没有办法跟踪你在页面中的位置(你的 id 是什么)?
    • @IanEdington 我现在明白你的意思了。刚刚更新了我的答案,希望对您有所帮助。
    • 这看起来很棒。这个周末我会试一试,然后告诉你进展如何!
    【解决方案3】:

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

    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。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2021-12-17
      • 2015-04-20
      • 1970-01-01
      • 2012-02-23
      • 1970-01-01
      • 2012-05-20
      相关资源
      最近更新 更多