检测窗口垂直滚动条何时出现

时间:2010-02-01 10:32:33

标签: javascript jquery resize scrollbar

是否有一个简单可靠的检测窗口垂直滚动条出现/消失的解决方案?

在JavaScript DOM操作页面变得足够高以显示滚动条之后,不会触发

window.onresize

在这个非常相似的帖子Detect if a page has a vertical scrollbar中描述了如何检测滚动条是否存在的解决方案,但我需要知道它何时出现。

8 个答案:

答案 0 :(得分:36)

很抱歉从死里复活,但我刚刚遇到这个限制,并提出了我自己的解决方案。这有点hacky但坚持我...

我们的想法是向页面添加100%宽度的不可见iframe,并在其内部窗口上侦听调整大小事件。这些事件不仅会更改外部窗口的大小,还会在外部窗口中添加或删除滚动条时更改。

它会触发常规窗口调整大小事件,因此如果您已经在侦听窗口调整大小,则不需要额外的代码。

在IE9和Chrome / Firefox最新测试中 - 可能可以在较旧的IE中使用,但我的项目不支持那些,所以我没有尝试过。

https://gist.github.com/OrganicPanda/8222636

答案 1 :(得分:17)

Window.onresize无效,因为窗口没有调整大小。

body.onresize无效,因为resize仅针对窗口和框架实施。

This question处理同一问题。顶级回答者有一些有趣的想法,虽然不是简单的,或跨浏览器。

我认为Rick Strahl基于Jquery的方法是你能得到的最好的方法:Monitoring Html Element CSS Changes in JavaScript它使用浏览器的“监视”功能(如果可用),或者如果没有则使用计时器。后者不是非常资源友好,但似乎没有办法解决它。

顺便提一下resize事情解决之后如何告诉滚动条的好方法是this question顺便提一下,以防您在提问时没有提到那个。

答案 2 :(得分:8)

根据OrganicPanda的回答,提出了这个jquery的事情

$('<iframe id="scrollbar-listener"/>').css({
    'position'      : 'fixed',
'width'         : '100%',
'height'        : 0, 
'bottom'        : 0,
'border'        : 0,
'background-color'  : 'transparent'
}).on('load',function() {
    var vsb     = (document.body.scrollHeight > document.body.clientHeight);
    var timer   = null;
    this.contentWindow.addEventListener('resize', function() {
        clearTimeout(timer);
        timer = setTimeout(function() {
            var vsbnew = (document.body.scrollHeight > document.body.clientHeight);
            if (vsbnew) {
                if (!vsb) {
                    $(top.window).trigger('scrollbar',[true]);
                    vsb=true;
                }
            } else {
                if (vsb) {
                    $(top.window).trigger('scrollbar',[false]);
                    vsb=false;
                }
            }
        }, 100);
    });
}).appendTo('body');

这将触发滚动条&#39;窗口上的事件,如果它们出现/消失

至少适用于chrome / mac。现在,有人将其扩展为检测水平滚动条: - )

答案 3 :(得分:4)

如果您正在使用AngularJS,您可以使用指令来检测宽度何时发生变化(假设出现/消失的滚动条是垂直滚动条):

app.directive('verticalScroll', function($rootScope){
    return {
        restrict: 'A',
        link: function (scope, element) {
            scope.$watch(
                function() {
                    return element[0].clientWidth;
                },
                function() {
                    $rootScope.$emit('resize');
                }
            );
        }
    }
});

这将在根作用域上触发一个事件,其他指令或控制器可以监听它。

手表是由角度摘要循环触发的,所以这依赖于Angular加载/删除了导致滚动条出现/消失的额外内容。

答案 4 :(得分:3)

The Scoop

通过使用ResizeObserver检查可能需要滚动条的元素大小的变化以及内容大小的变化,可以检测滚动条可见性的变化。

原理

我开始使用<iframe>方法实现解决方案,但很快发现完整的实现需要打破我的应用程序视图之间的关注点分离。我有一个父视图,需要知道子视图何时获取垂直滚动条。 (我不关心水平滚动条。)我有两种情况可能会影响垂直滚动条的可见性:

  1. 调整父视图的大小。这是由用户直接控制的。

  2. 子视图的内容变得更大或更小。这是在用户的间接控制下。子视图显示搜索结果。结果的数量和类型决定了子视图的大小。

  3. 我发现,如果我使用<iframe>,我必须使用子视图来支持父母的需求。我更喜欢孩子不包含纯粹父母关心的东西的代码。使用我在此描述的解决方案,只需要修改父视图。

    因此,在寻找更好的解决方案时,我找到了Daniel Herr的this answer。他建议使用ResizeObserver来检测div的尺寸何时发生变化。 ResizeObserver不是本地可用于浏览器,但有一个强大的ponyfill / polyfill,我用它来支持本机支持不可用的情况。 (以下是ResizeObserver的{​​{3}}。)

    证明的概念

    我在其ponyfill模式下使用spec。这样,全球环境仍未受到影响。此实现依赖于window.requestAnimationFrame,并且对于不支持setTimeout的平台,将依赖window.requestAnimationFrame。看this polyfill“我可以使用......吗?”,我看到的并没有打扰我。 YMMV。

    我有一个活support for requestAnimationFrame。关键是在DOM元素上侦听大小的变化,这些变化可以接受滚动条(标识为container的元素,绿色)并监听可能需要滚动的内容的大小变化(具有id的元素) content)。概念验证使用interact.js来管理调整大小元素(标识为resizer,蓝色),以允许调整container的大小。如果您拖动resizer的右下角,则会调整resizercontainer的大小。这两个按钮允许模拟container显示的内容大小的变化。

    我在目前处于预发布阶段的代码中使用此方法,这意味着它在多个浏览器上通过了测试,并且正在由利益相关者进行评估,但在生产中尚未

    HTML:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="interact.js@*" data-semver="1.0.26" src="//rawgit.com/taye/interact.js/v1.0.26/interact.js"></script>
      <script src="//rawgit.com/que-etc/resize-observer-polyfill/master/dist/ResizeObserver.global.js"></script>
      <link rel="stylesheet" href="style.css" />
    </head>
    
    <body>
      <div id="resizer">
        <div id="container">
          <ul id="content">
            <li>Something</li>
          </ul>
        </div>
      </div>
      <button id="add">Add to content</button>
      <button id="remove">Remove from content</button>
      <p>Scroll bar is: <span id="visibility"></span></p>
      <ul id="event-log"></ul>
      <script src="script.js"></script>
    </body>
    
    </html>
    

    JavaScript:

    var container = document.getElementById("container");
    var resizer = document.getElementById("resizer");
    interact(resizer)
      .resizable({
        restrict: {
          restriction: {
            left: 0,
            top: 0,
            right: window.innerWidth - 10,
            bottom: window.innerHeight - 10
          }
        }
      })
      .on('resizemove', function(event) {
        var target = resizer;
    
        var rect = target.getBoundingClientRect();
    
        var width = rect.width + event.dx;
        var height = rect.height + event.dy;
        target.style.width = width + 'px';
        target.style.height = height + 'px';
      });
    
    var content = document.getElementById("content");
    var add = document.getElementById("add");
    add.addEventListener("click", function() {
      content.insertAdjacentHTML("beforeend", "<li>Foo</li>");
    });
    
    var remove = document.getElementById("remove");
    remove.addEventListener("click", function() {
      content.removeChild(content.lastChild);
    });
    
    // Here is the code that pertains to the scrollbar visibility
    
    var log = document.getElementById("event-log");
    content.addEventListener("scrollbar", function () {
      log.insertAdjacentHTML("beforeend", "<li>Scrollbar changed!</li>");
    });
    
    var visiblity = document.getElementById("visibility");
    var previouslyVisible;
    function refreshVisibility() {
      var visible = container.scrollHeight > container.clientHeight;
      visibility.textContent = visible ? "visible" : "not visible";
      if (visible !== previouslyVisible) {
        content.dispatchEvent(new Event("scrollbar"));
      }
      previouslyVisible = visible;
    }
    // refreshVisibility();
    
    
    var ro = new ResizeObserver(refreshVisibility);
    ro.observe(container);
    ro.observe(content);
    

    CSS:

    * {
      box-sizing: border-box;
    }
    
    #container {
      position: relative;
      top: 10%;
      left: 10%;
      height: 80%;
      width: 80%;
      background: green;
      overflow: auto;
    }
    
    #resizer {
      background: blue;
      height: 200px;
      width: 200px;
    }
    

答案 5 :(得分:0)

这与何时有关,您需要确定滚动条的可见性。

OP所说的时间是“ JavaScript DOM操作之后”。如果您的代码中发生了这种操作,那么该是检查滚动条是否可见的时候了。为什么还需要一个活动?您怎么知道这种DOM操作何时发生?

我意识到这是一个老问题,但是我现在只是在一个纯JavaScript项目中处理这个问题,而且知道何时检查滚动条的可见性也没有问题。触发用户事件或触发系统事件,而且我知道DOM操作何时发生,因为我是通过javascript引起的。我看不到JavaScript DOM操作超出我的代码意识的情况。

也许scrollbarVisibilityChange事件会很方便,但是绝对没有必要。 9年后,这让我震惊。我想念什么吗?

答案 6 :(得分:0)

如果您只需要在Windows 浏览器(IE 除外)上检测滚动外观,这里以Resize Observer API 用于垂直滚动为例。 >

想法

  • <div>position: fixed 附加到 <body>
  • 使其100% 宽度观察尺寸变化
  • 滚动的外观减少<div>宽度,进而调用观察者回调。立>

为什么只有 Windows 浏览器?

移动和 macOS 浏览器有一个消失的滚动条,从文档流中取出,不会影响页面布局。

为什么位置应该是fixed而不是absolute

带有 position: fixed 的元素相对于视口建立的初始包含块定位。

position: absolute 可能会失败,如果 <body> 也是绝对定位的并且宽度与视口不同。

const innerWidthFiller = document.createElement('div')
innerWidthFiller.style.cssText = 'position: fixed; left: 0; right: 0'
document.body.appendChild(innerWidthFiller)

const detectScroll = () => {
  const {clientHeight, scrollHeight} = document.documentElement
  window.result.value = scrollHeight > clientHeight
}

const resizeObserver = new ResizeObserver(detectScroll)
resizeObserver.observe(innerWidthFiller)
#test {
  border: 1px solid;
  white-space: nowrap;
}

output {
  font-weight: bold;
}
<button onclick="test.style.fontSize='100vh'">Enlarge the text</button>
<button onclick="test.style.fontSize=''">Reset</button>
Page scroll state: <output id="result"></output>

<hr>

<span id="test">Test element</span>

答案 7 :(得分:-1)

动态检测浏览器垂直滚动条事件 比较window.innerWidth到getBoundingClientRect() 使用Javascript的DIV元素。测试最新 IE FF Chrome。请参阅文档here