2022年6月26日

ウィンドウサイズとスクロール

ブラウザウィンドウの幅と高さをどのように取得すればよいでしょうか?スクロールアウトした部分を含め、ドキュメントの全幅と高さをどのように取得すればよいでしょうか?JavaScript を使用してページをどのようにスクロールすればよいでしょうか?

このタイプの情報については、<html> タグに対応するルートドキュメント要素 document.documentElement を使用できます。ただし、考慮すべき追加のメソッドと特殊性があります。

ウィンドウの幅/高さ

ウィンドウの幅と高さを取得するには、document.documentElementclientWidth/clientHeight を使用できます。

たとえば、このボタンはウィンドウの高さを表示します。

window.innerWidth/innerHeight ではない

ブラウザは、window.innerWidth/innerHeight のようなプロパティもサポートしています。それらは私たちが求めているもののように見えるので、代わりにそれらを使用しないのはなぜですか?

スクロールバーが存在し、それがいくつかのスペースを占めている場合、clientWidth/clientHeight はそれなしで(それを差し引いて)幅/高さを提供します。言い換えれば、コンテンツで使用可能なドキュメントの可視部分の幅/高さを返します。

window.innerWidth/innerHeight はスクロールバーを含みます。

スクロールバーが存在し、それがいくつかのスペースを占めている場合、これら2行は異なる値を表示します。

alert( window.innerWidth ); // full window width
alert( document.documentElement.clientWidth ); // window width minus the scrollbar

ほとんどの場合、スクロールバー内(もしあれば)に何かを描画または配置するために、利用可能なウィンドウ幅が必要になるため、documentElement.clientHeight/clientWidth を使用する必要があります。

DOCTYPE が重要

注意: HTML に <!DOCTYPE HTML> がない場合、トップレベルのジオメトリプロパティは少し異なる動作をする可能性があります。奇妙なことが起こる可能性があります。

最新の HTML では、常に DOCTYPE を記述する必要があります。

ドキュメントの幅/高さ

理論的には、ルートドキュメント要素が document.documentElement であり、すべてのコンテンツを囲んでいるため、ドキュメントの全サイズを document.documentElement.scrollWidth/scrollHeight として測定できます。

ただし、その要素では、ページ全体に対して、これらのプロパティは意図したとおりに機能しません。Chrome/Safari/Opera では、スクロールがない場合、documentElement.scrollHeightdocumentElement.clientHeight よりも小さくなることさえあります!奇妙ですよね?

ドキュメントの全高を確実に取得するには、これらのプロパティの最大値を取得する必要があります。

let scrollHeight = Math.max(
  document.body.scrollHeight, document.documentElement.scrollHeight,
  document.body.offsetHeight, document.documentElement.offsetHeight,
  document.body.clientHeight, document.documentElement.clientHeight
);

alert('Full document height, with scrolled out part: ' + scrollHeight);

なぜそうなのでしょうか?聞かない方がいいでしょう。これらの矛盾は、古い時代から来ており、「スマート」なロジックではありません。

現在のスクロールを取得

DOM要素には、その scrollLeft/scrollTop プロパティに現在のスクロール状態があります。

ドキュメントのスクロールでは、document.documentElement.scrollLeft/scrollTop はほとんどのブラウザで機能しますが、Safari のような古い WebKit ベースのブラウザ (バグ 5991) を除き、document.documentElement の代わりに document.body を使用する必要があります。

幸いなことに、スクロールは特別なプロパティ window.pageXOffset/pageYOffset で利用できるため、これらの特殊性をまったく覚える必要はありません。

alert('Current scroll from the top: ' + window.pageYOffset);
alert('Current scroll from the left: ' + window.pageXOffset);

これらのプロパティは読み取り専用です。

window プロパティ scrollX および scrollY としても利用可能です。

歴史的な理由により、両方のプロパティが存在しますが、それらは同じです。

  • window.pageXOffsetwindow.scrollX のエイリアスです。
  • window.pageYOffsetwindow.scrollY のエイリアスです。

スクロール: scrollTo, scrollBy, scrollIntoView

重要

JavaScript でページをスクロールするには、その DOM が完全に構築されている必要があります。

たとえば、<head> のスクリプトでページをスクロールしようとしても、うまくいきません。

通常の要素は、scrollTop/scrollLeft を変更することでスクロールできます。

document.documentElement.scrollTop/scrollLeft を使用してページでも同じことができます (Safari を除く。代わりに document.body.scrollTop/Left を使用する必要があります)。

または、よりシンプルでユニバーサルなソリューションがあります。特別なメソッド window.scrollBy(x,y) および window.scrollTo(pageX,pageY) です。

  • メソッド scrollBy(x,y) は、ページの *現在の位置からの相対* でページをスクロールします。たとえば、scrollBy(0,10) はページを 10px 下にスクロールします。

    下のボタンでこれをデモします。

  • メソッド scrollTo(pageX,pageY) は、ページを *絶対座標* にスクロールし、可視部分の左上隅が、ドキュメントの左上隅に対する座標 (pageX, pageY) を持つようにします。これは、scrollLeft/scrollTop を設定するようなものです。

    先頭にスクロールするには、scrollTo(0,0) を使用できます。

これらのメソッドはすべてのブラウザで同じように機能します。

scrollIntoView

完全を期すために、もう1つのメソッドについて説明しましょう: elem.scrollIntoView(top)

elem.scrollIntoView(top) の呼び出しは、elem が表示されるようにページをスクロールします。これには1つの引数があります。

  • top=true の場合 (これがデフォルトです)、ページは elem がウィンドウの上部に表示されるようにスクロールされます。要素の上端がウィンドウの上部に揃えられます。
  • top=false の場合、ページは elem が下部に表示されるようにスクロールされます。要素の下端がウィンドウの下部に揃えられます。

下のボタンは、ウィンドウの上部に自身を配置するためにページをスクロールします。

そして、このボタンは、ウィンドウの下部に自身を配置するためにページをスクロールします。

スクロールの禁止

ドキュメントを「スクロール不可」にする必要がある場合があります。たとえば、すぐに注意を必要とする大きなメッセージでページを覆い、訪問者にドキュメントではなくそのメッセージを操作させたい場合などです。

ドキュメントをスクロール不可にするには、document.body.style.overflow = "hidden" を設定するだけで十分です。ページは現在のスクロール位置で「フリーズ」します。

試してみてください。

最初のボタンはスクロールをフリーズし、2番目のボタンはそれを解除します。

document.body だけでなく、他の要素のスクロールをフリーズするために同じ手法を使用できます。

この方法の欠点は、スクロールバーが消えることです。スクロールバーがいくらかのスペースを占めていた場合、そのスペースが解放され、コンテンツがそれを埋めるために「ジャンプ」します。

それは少し奇妙に見えますが、フリーズ前後の clientWidth を比較すれば回避できます。増加した場合(スクロールバーが消えた)、コンテンツ幅を同じに保つために、スクロールバーの代わりに document.bodypadding を追加します。

まとめ

ジオメトリ

  • ドキュメントの可視部分の幅/高さ (コンテンツ領域の幅/高さ): document.documentElement.clientWidth/clientHeight

  • スクロールアウトした部分を含むドキュメント全体の幅/高さ

    let scrollHeight = Math.max(
      document.body.scrollHeight, document.documentElement.scrollHeight,
      document.body.offsetHeight, document.documentElement.offsetHeight,
      document.body.clientHeight, document.documentElement.clientHeight
    );

スクロール

  • 現在のスクロールを読み取る: window.pageYOffset/pageXOffset

  • 現在のスクロールを変更する

    • window.scrollTo(pageX,pageY) – 絶対座標、
    • window.scrollBy(x,y) – 現在の位置からの相対的なスクロール、
    • elem.scrollIntoView(top)elem が表示されるようにスクロール (ウィンドウの上/下に合わせる)。
チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善する点について提案がある場合は、コメントする代わりに GitHub issue を送信するか、プルリクエストを送信してください。
  • 記事の何かを理解できない場合は、詳しく説明してください。
  • コードを数語挿入するには <code> タグを使用し、数行の場合は <pre> タグでラップし、10行以上場合はサンドボックス (plnkr, jsbin, codepen…) を使用してください。