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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2175992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:06:18  来源:igfitidea点击:

Detect when window vertical scrollbar appears

javascriptjqueryresizescrollbar

提问by Roman

Is there a simple and reliable solution for detecting window vertical scrollbar appears/disappears?

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

window.onresizeisn't triggered when after JavaScript DOM manipulation page becomes high enough for appearing scrollbar.

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

In this very similar post Detect if a page has a vertical scrollbardescribed solution how to detect whether scrollbar is present or not, but I need to know when exactly it appears.

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

回答by OrganicPanda

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. 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.

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

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

回答by commonpike

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. now, someone extend this to detect horizontal scrollbars :-)

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

回答by Dan King

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 已加载/删除导致滚动条出现/消失的额外内容。

回答by Louis

The Scoop

独家新闻

It is possible to detect changes in scrollbar visibility by using ResizeObserverto 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. 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:

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

  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.

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

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

I found that if I used <iframe>I'd have to muck with the child view to support the parent's needs. 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.

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

So in looking for a better solution, I found this answerby Daniel Herr. He suggests using ResizeObserverto detect when a div's dimensions change. ResizeObserveris not yetavailable natively across browsers but there is a robust ponyfill/polyfill that I use for support in cases where native support is not available. (Here is the specfor ResizeObserver.)

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

Proof-of-Concept

概念验证

I use this polyfillin its ponyfill mode. That way, the global environment remains untouched. This implementation relies on window.requestAnimationFrame, and will fall back on setTimeoutfor platforms that don't support window.requestAnimationFrame. Looking at the support for requestAnimationFrameon "Can I use...?", what I see there does not bother me. YMMV.

我在它的 ponyfill 模式下使用这个polyfill。这样,全球环境就不会受到影响。此实现依赖于window.requestAnimationFrame,并且setTimeout对于不支持window.requestAnimationFrame. 纵观支持requestAnimationFrame的“我可以使用...?”,我看到那里我并不介意。天啊。

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). The proof-of-concept uses interact.jsto manage a resizer element (with id resizer, in blue) that allows resizing container. If you drag the bottom right corner of resizer, it will resize both resizerand container. The two buttons allow simulating changes in the size of the contents displayed by container.

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

回答by jamess

It's all about whenyou need to determine the scrollbar's visibility.

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

The OP speaks of a time "after JavaScript DOM manipulation". 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?

OP 谈到了“在 JavaScript DOM 操作之后”的时间。如果该操作发生在您的代码中,那么就是检查滚动条是否可见的时候了。除此之外,你为什么还需要一个事件?你怎么不知道这个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. 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. I don't see a case where that javascript DOM manipulation is outside of my code's awareness.

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

Maybe a scrollbarVisibilityChange event would be convenient, but it's certainly not necessary. This strikes me as a non-issue, 9 years later. Am I missing something?

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

回答by Dan Taylor

Dynamically Detect Browser Vertical Scrollbar Event by comparing window.innerWidth to getBoundingClientRect() of a DIV element using Javascript. Tested with latest IE FF Chrome. See documentation here

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