簡體   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