web-dev-qa-db-ja.com

ウィンドウの垂直スクロールバーがいつ表示されるかを検出する

ウィンドウの垂直スクロールバーの表示/非表示を検出するためのシンプルで信頼できるソリューションはありますか?

window.onresizeは、JavaScript DOM操作ページがスクロールバーを表示するのに十分な高さになった後はトリガーされません。

この非常に類似した投稿 ページに垂直スクロールバーがあるかどうかを検出する ソリューションは、スクロールバーが存在するかどうかを検出する方法を説明しましたが、正確に表示されるタイミングを知る必要があります。

36
Roman

Window.onresizeウィンドウのサイズが変更されていないため、機能しません。

body.onresizeは機能しません。resizeはウィンドウとフレームに対してのみ実装されているためです。

この質問 は同じ問題を扱います。トップアンサーには、いくつかの興味深いアイデアがありますが、単純なものやクロスブラウザーはありません。

私はRick StrahlによるこのJqueryベースのアプローチがあなたが得ることができる最高のものだと思います: JavaScriptでのHtml要素のCSS変更の監視 それは使用します利用可能な場合はブラウザの「ウォッチ」機能、利用できない場合はタイマー。後者はあまりリソー​​スに優しくありませんが、それを回避する方法はないようです。

resizeの問題が解決されたときにスクロールバーを確認する良い方法は、念のために this question にありますあなたはあなたの質問でそれについて言及していません。

18
Pekka

これを死から復活させて申し訳ありませんが、私はこの制限に遭遇し、独自の解決策を考え出しました。少しハッキーですが、私にこだわってください...

アイデアは、ページに100%幅の非表示のiframeを追加し、その内部ウィンドウでサイズ変更イベントをリッスンすることです。これらのイベントは、外側のウィンドウのサイズだけでなく、外側のウィンドウにスクロールバーが追加または削除されたときにも変更を取得します。

通常のウィンドウサイズ変更イベントをトリガーするので、既にウィンドウサイズ変更をリッスンしている場合は、追加のコードは必要ありません。

IE9と最新のChrome/Firefoxでテスト済み-おそらく古いIEで動作するようにできるかもしれませんが、私のプロジェクトはそれらをサポートしていないため、試していません。

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

38
OrganicPanda

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で動作します。今、誰かがこれを拡張して水平スクロールバーを検出します:-)

8
commonpike

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がスクロールバーを表示/非表示にする原因となった余分なコンテンツをロード/削除したことに依存しています。

4
Dan King

スクープ

ResizeObserverを使用してスクロールバーの可視性の変化を検出し、スクロールバーを取得する要素のサイズの変化とそのコンテンツのサイズの変化を確認することができます。

根拠

<iframe>メソッドを使用してソリューションの実装を開始しましたが、完全な実装を行うには、アプリケーションのビュー間の懸念の分離を壊す必要があることがすぐにわかりました。子ビューが垂直スクロールバーを取得するタイミングを知る必要がある親ビューがあります。 (水平スクロールバーは気にしません。)垂直スクロールバーの可視性に影響を与える可能性のある2つの状況があります。

  1. 親ビューのサイズが変更されます。これはユーザーの直接の制御下にあります。

  2. 子ビューの内容が大きくまたは小さくなります。これは、ユーザーの間接的な制御下にあります。子ビューに検索結果が表示されています。結果の数とタイプによって、子ビューのサイズが決まります。

<iframe>を使用した場合、親のニーズをサポートするために子ビューをいじくる必要があることがわかりました。純粋に親の関心事である何かのためのコードを子供が含まないことを私は好む。ここで説明するソリューションでは、親ビューのみを変更する必要がありました。

だから、より良い解決策を探していたときに、ダニエル・ヘルによる この答え が見つかりました。彼はResizeObserverを使用してdivの次元がいつ変化するかを検出することを提案しています。 ResizeObserverまだ使用できませんブラウザー間でネイティブに使用できますが、ネイティブサポートがある場合のサポートに使用する堅牢なポニーフィル/ポリフィルがあります利用不可。 (これがResizeObserverの-​​ spec です。)

コンセプトの証明

私はポニーフィルモードで this polyfill を使用しています。そのようにして、地球環境は手つかずのままです。この実装はwindow.requestAnimationFrameに依存しており、window.requestAnimationFrameをサポートしないプラットフォームではsetTimeoutにフォールバックします。 support for requestAnimationFrame on "Can I use ...?"を見ると、そこにあるものは気になりません。 YMMV。

私はライブ 概念実証 を持っています。重要なのは、スクロールバーを受け入れることができるDOM要素(緑色のcontainerの要素)のサイズの変化をリッスンし、スクロールが必要なコンテンツ(要素のある要素)のサイズの変化をリッスンすることです。 id content)。概念実証ではinteract.jsを使用して、resizerのサイズ変更を可能にするリサイザー要素(IDはcontainer、青色)を管理します。 resizerの右下隅をドラッグすると、resizercontainerの両方のサイズが変更されます。 2つのボタンを使用すると、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;
}
3
Louis

whenについてのすべてです。スクロールバーの可視性を決定する必要があります。

OPは、「JavaScript DOM操作後」の時間について話します。コードでその操作が発生した場合は、その時点で、スクロールバーが表示されているかどうかを確認します。それに加えてなぜイベントが必要なのですか?このDOM操作がいつ発生するか分からないのはなぜですか?

これは古い質問であることに気づきましたが、今は純粋なjavascriptプロジェクトでこれを扱っています。スクロールバーの可視性をいつチェックするかについては問題ありません。ユーザーイベントが発生するか、システムイベントが発生し、JavaScriptによってDOM操作が発生したため、いつDOM操作が発生したかがわかります。そのJavaScript DOMの操作が私のコードの認識の外にあるケースは見ません。

おそらくscrollbarVisibilityChangeイベントが便利でしょうが、それは確かに必要ではありません。これは、9年後、私は問題ではないと思います。何か不足していますか?

0
jamess