简体   繁体   English

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

[英]Detect when window vertical scrollbar appears

Is there a simple and reliable solution for detecting window vertical scrollbar appears/disappears?是否有一个简单可靠的解决方案来检测窗口垂直滚动条的出现/消失?

window.onresize isn't triggered when after JavaScript DOM manipulation page becomes high enough for appearing scrollbar.当 JavaScript DOM 操作页面变得足够高以显示滚动条时,不会触发window.onresize

In this very similar post Detect if a page has a vertical scrollbar described solution how to detect whether scrollbar is present or not, but I need to know when exactly it appears.在这篇非常相似的文章检测页面是否有垂直滚动条中,描述了如何检测滚动条是否存在的解决方案,但我需要知道它何时出现。

Sorry to bring this back from the dead but I have just run in to this limitation and came up with my own solution.很抱歉让这个死而复生,但我刚刚遇到了这个限制并想出了我自己的解决方案。 It's a bit hacky but stick with me ...这有点hacky但坚持我......

The idea is to add a 100% width invisible iframe to the page and listen for resize events on it's internal window.这个想法是向页面添加一个 100% 宽度的不可见 iframe,并在其内部窗口上侦听调整大小事件。 These events will pick up changes not only to the outer window's size but also when scrollbars get added to or removed from the outer window.这些事件不仅会改变外部窗口的大小,还会在滚动条添加到外部窗口或从外部窗口中删除时获取更改。

It triggers a regular window resize event so it requires no extra code if you are already listening for window resize.它会触发一个常规的窗口调整大小事件,因此如果您已经在监听窗口调整大小,则不需要额外的代码。

Tested in IE9 and Chrome/Firefox latest - could maybe be made to work in older IEs but my project doesn't support those so I haven't tried.最新在 IE9 和 Chrome/Firefox 中进行了测试 - 也许可以在较旧的 IE 中工作,但我的项目不支持这些,所以我还没有尝试过。

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

Window.onresize won't work, because the window isn't resizing. Window.onresize将不起作用,因为窗口没有调整大小。

body.onresize won't work, as resize is implemented only for windows and frames. body.onresize将不起作用,因为resize仅针对窗口和框架实现。

This question deals with the same issue. 这个问题涉及同样的问题。 The top answerer has some interesting ideas, though none simple, or cross-browser. 顶级回答者有一些有趣的想法,虽然不是简单的,或跨浏览器。

I think this Jquery based approach by Rick Strahl is the best you can get: Monitoring Html Element CSS Changes in JavaScript it uses the browser's "watch" functions if available, or a timer if not. 我认为Rick Strahl基于Jquery的方法是你能得到的最好的方法: 监控Html元素CSS在JavaScript中的变化它使用浏览器的“监视”功能(如果可用),或者如果没有则使用计时器。 The latter is not very resource friendly but there seems to be no way around it. 后者不是非常资源友好,但似乎没有办法解决它。

A good way how to tell the scrollbar once the resize thing is resolved is in this question by the way, just in case you aren't referring to that one in your question. 顺便提一下,一旦解决了resize事情,如何告诉滚动条的一个好方法就是这个问题 ,以防你在问题中没有提到那个问题。

Based on OrganicPanda's answer, came up with this jquery thing基于 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');

This will trigger 'scrollbar' events on the window, if they appear/dissapear如果它们出现/消失,这将触发窗口上的“滚动条”事件

Works on chrome/mac, at least.至少适用于 chrome/mac。 now, someone extend this to detect horizontal scrollbars :-)现在,有人扩展它来检测水平滚动条:-)

The Scoop独家新闻

It is possible to detect changes in scrollbar visibility by using ResizeObserver to check for changes in the size of the element that may take scrollbars and changes in the size of its contents.通过使用ResizeObserver检查可能采用滚动条的元素大小的变化及其内容大小的变化,可以检测滚动条可见性的变化。

Rationale基本原理

I started implementing a solution with the <iframe> method but quickly found that having a complete implementation required breaking the separation of concerns among the views of my application.我开始使用<iframe>方法实现一个解决方案,但很快发现要实现完整的实现需要打破应用程序视图之间的关注点分离。 I have a parent view which needs to know when a child view acquires a vertical scrollbar.我有一个父视图,它需要知道子视图何时获得垂直滚动条。 (I don't care about the horizontal scrollbar.) I have two situations that may affect the visibility of the vertical scrollbar: (我不关心水平滚动条。)我有两种情况可能会影响垂直滚动条的可见性:

  1. The parent view is resized.父视图被调整大小。 This is under direct control of the user.这是由用户直接控制的。

  2. The child view's contents becomes bigger or smaller.子视图的内容变得更大或更小。 This is under indirect control of the user.这是在用户的间接控制之下。 The child view is showing the results of a search.子视图显示搜索结果。 The quantity and type of results determine the size of the child view.结果的数量和类型决定了子视图的大小。

I found that if I used <iframe> I'd have to muck with the child view to support the parent's needs.我发现如果我使用<iframe>我就不得不处理子视图来支持父视图的需求。 I prefer the child to not contain code for something which is purely a concern of the parent.我更喜欢孩子不包含纯粹是父母关心的事情的代码。 With the solution I describe here, only the parent view needed to be modified.使用我在这里描述的解决方案,只需要修改父视图。

So in looking for a better solution, I found this answer by Daniel Herr.因此,在寻找更好的解决方案时,我找到了 Daniel Herr 的答案 He suggests using ResizeObserver to detect when a div's dimensions change.他建议使用ResizeObserver来检测 div 的尺寸何时发生变化。 ResizeObserver is not yet available natively across browsers but there is a robust ponyfill/polyfill that I use for support in cases where native support is not available. ResizeObserver尚未在浏览器中本地可用,但有一个强大的 ponyfill/polyfill 可用于在本地支持不可用的情况下提供支持。 (Here is the spec for ResizeObserver .) (这是ResizeObserver规范。)

Proof-of-Concept概念验证

I use this polyfill in its ponyfill mode.我在它的 ponyfill 模式下使用这个polyfill。 That way, the global environment remains untouched.这样,全球环境就不会受到影响。 This implementation relies on window.requestAnimationFrame , and will fall back on setTimeout for platforms that don't support window.requestAnimationFrame .此实现依赖于window.requestAnimationFrame ,并且对于不支持window.requestAnimationFrame平台将回退到setTimeout Looking at the support for requestAnimationFrame on "Can I use...?", what I see there does not bother me.查看“我可以使用...?”requestAnimationFrame支持,我在那里看到的并没有打扰我。 YMMV.天啊。

I have a live proof-of-concept .我有一个实时的概念验证 The key is to listen to changes in size on the DOM element that can accept scroll bars (the element with id container , in green) and listen to changes in size on the content that may need scrolling (the element with id content ).关键是监听可以接受滚动条的 DOM 元素(带有 id container的元素,绿色的元素)的大小变化,并监听可能需要滚动的内容(带有 id content的元素)的大小变化。 The proof-of-concept uses interact.js to manage a resizer element (with id resizer , in blue) that allows resizing container .概念验证使用interact.js来管理允许调整container大小的 resizer 元素(带有 id resizer ,蓝色)。 If you drag the bottom right corner of resizer , it will resize both resizer and container .如果拖动resizer右下角,它会同时调整resizercontainer大小。 The two buttons allow simulating changes in the size of the contents displayed by container .这两个按钮允许模拟container显示的内容大小的变化。

I'm using this method in code that is currently at a pre-release stage, meaning it passed tests on multiple browsers, and is being evaluated by stakeholders, but is not yet in production.我在当前处于预发布阶段的代码中使用此方法,这意味着它通过了在多个浏览器上的测试,并且正在接受利益相关者的评估,但尚未投入生产。

The HTML: 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>

The JavaScript: 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);

The CSS: 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;
}

If you're using AngularJS, you can use a directive to detect when the width changes (assuming the appearing/disappearing scrollbar is a vertical one):如果您使用的是 AngularJS,则可以使用指令来检测宽度何时发生变化(假设出现/消失的滚动条是垂直滚动条):

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

This fires an event on the root scope which other directives or controllers can listen for.这会在其他指令或控制器可以侦听的根作用域上触发一个事件。

The watch is fired by the angular digest loop, so this relies on Angular having loaded/removed the extra content which has caused your scrollbar to appear/disappear.监视由 angular 摘要循环触发,因此这依赖于 Angular 加载/删除了导致滚动条出现/消失的额外内容。

Dynamically Detect Browser Vertical Scrollbar Event by comparing window.innerWidth to getBoundingClientRect() of a DIV element using Javascript.通过使用 Javascript 将 window.innerWidth 与 DIV 元素的 getBoundingClientRect() 进行比较,动态检测浏览器垂直滚动条事件。 Tested with latest IE FF Chrome.使用最新的 IE FF Chrome 进行测试。 See documentation here请参阅此处的文档

It's all about when you need to determine the scrollbar's visibility.这完全是关于何时需要确定滚动条的可见性。

The OP speaks of a time "after JavaScript DOM manipulation". OP 谈到了“在 JavaScript DOM 操作之后”的时间。 If that manipulation happens in your code, then that's the time for checking if the scrollbar is visible.如果该操作发生在您的代码中,那么就是检查滚动条是否可见的时候了。 Why do you need an event in addition to that?除此之外,你为什么还需要一个事件? How is it that you don't know when this DOM manipulation occurs?你怎么不知道这个DOM操作什么时候发生?

I realize this is an old question, but I'm just now dealing with this in a pure javascript project, and I have no issue knowing when to check for scrollbar visibility.我意识到这是一个老问题,但我刚刚在一个纯 javascript 项目中处理这个问题,我知道何时检查滚动条可见性没有问题。 Either a user event fires, or a system event fires, and I know when the DOM manipulation occurs because I'm causing it via javascript.要么触发用户事件,要么触发系统事件,我知道 DOM 操作何时发生,因为我是通过 javascript 引起的。 I don't see a case where that javascript DOM manipulation is outside of my code's awareness.我没有看到 javascript DOM 操作超出我的代码意识的情况。

Maybe a scrollbarVisibilityChange event would be convenient, but it's certainly not necessary.也许 scrollbarVisibilityChange 事件会很方便,但肯定没有必要。 This strikes me as a non-issue, 9 years later. 9 年后,这让我觉得不是问题。 Am I missing something?我错过了什么吗?

If you only need to detect the scroll appearance on Windows browsers (except IE), here's my solution with Resize Observer API for vertical scroll as an example.如果您只需要在Windows 浏览器(IE 除外)上检测滚动外观,这里是我的解决方案,以垂直滚动的Resize Observer API为例。

Idea主意

  • Append <div> with position: fixed to <body><div>附加到position: fixed<body>
  • Make it 100% width and observe for size changes使其100% 宽度观察尺寸变化
  • The appearance of the scroll reduces the <div> 's width , which in turn calls the observer callback .滚动的外观减少<div>宽度,这反过来又调用了观察者回调

Why only Windows browsers?为什么只有 Windows 浏览器?

Mobile and macOS browsers have a disappearing scroll that is taken out of the document flow and doesn't affect the page layout.移动和 macOS 浏览器具有从文档流中取出的消失滚动条,并且不会影响页面布局。

Why should the position be fixed and not absolute ?为什么位置应该是fixed而不是absolute

Element with position: fixed is positioned relative to the initial containing block established by the viewport .带有position: fixed元素相对于视口建立的初始包含块定位。

position: absolute may fail if the <body> is also absolutely positioned and has a different width than the viewport. 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