繁体   English   中英

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

[英]Detect when window vertical scrollbar appears

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

当 JavaScript DOM 操作页面变得足够高以显示滚动条时,不会触发window.onresize

在这篇非常相似的文章检测页面是否有垂直滚动条中,描述了如何检测滚动条是否存在的解决方案,但我需要知道它何时出现。

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

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

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

最新在 IE9 和 Chrome/Firefox 中进行了测试 - 也许可以在较旧的 IE 中工作,但我的项目不支持这些,所以我还没有尝试过。

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

Window.onresize将不起作用,因为窗口没有调整大小。

body.onresize将不起作用,因为resize仅针对窗口和框架实现。

这个问题涉及同样的问题。 顶级回答者有一些有趣的想法,虽然不是简单的,或跨浏览器。

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

顺便提一下,一旦解决了resize事情,如何告诉滚动条的一个好方法就是这个问题 ,以防你在问题中没有提到那个问题。

基于 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');

如果它们出现/消失,这将触发窗口上的“滚动条”事件

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

独家新闻

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

基本原理

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

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

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

我发现如果我使用<iframe>我就不得不处理子视图来支持父视图的需求。 我更喜欢孩子不包含纯粹是父母关心的事情的代码。 使用我在这里描述的解决方案,只需要修改父视图。

因此,在寻找更好的解决方案时,我找到了 Daniel Herr 的答案 他建议使用ResizeObserver来检测 div 的尺寸何时发生变化。 ResizeObserver尚未在浏览器中本地可用,但有一个强大的 ponyfill/polyfill 可用于在本地支持不可用的情况下提供支持。 (这是ResizeObserver规范。)

概念验证

我在它的 ponyfill 模式下使用这个polyfill。 这样,全球环境就不会受到影响。 此实现依赖于window.requestAnimationFrame ,并且对于不支持window.requestAnimationFrame平台将回退到setTimeout 查看“我可以使用...?”requestAnimationFrame支持,我在那里看到的并没有打扰我。 天啊。

我有一个实时的概念验证 关键是监听可以接受滚动条的 DOM 元素(带有 id container的元素,绿色的元素)的大小变化,并监听可能需要滚动的内容(带有 id content的元素)的大小变化。 概念验证使用interact.js来管理允许调整container大小的 resizer 元素(带有 id resizer ,蓝色)。 如果拖动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;
}

如果您使用的是 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 摘要循环触发,因此这依赖于 Angular 加载/删除了导致滚动条出现/消失的额外内容。

通过使用 Javascript 将 window.innerWidth 与 DIV 元素的 getBoundingClientRect() 进行比较,动态检测浏览器垂直滚动条事件。 使用最新的 IE FF Chrome 进行测试。 请参阅此处的文档

这完全是关于何时需要确定滚动条的可见性。

OP 谈到了“在 JavaScript DOM 操作之后”的时间。 如果该操作发生在您的代码中,那么就是检查滚动条是否可见的时候了。 除此之外,你为什么还需要一个事件? 你怎么不知道这个DOM操作什么时候发生?

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

也许 scrollbarVisibilityChange 事件会很方便,但肯定没有必要。 9 年后,这让我觉得不是问题。 我错过了什么吗?

如果您只需要在Windows 浏览器(IE 除外)上检测滚动外观,这里是我的解决方案,以垂直滚动的Resize Observer API为例。

主意

  • <div>附加到position: fixed<body>
  • 使其100% 宽度观察尺寸变化
  • 滚动的外观减少<div>宽度,这反过来又调用了观察者回调

为什么只有 Windows 浏览器?

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

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

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

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

 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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM