【JavaScript】スクロール操作に関する基礎知識

JavaScript

スクロールに関するプロパティ

スクロールを操作する際に覚えておくとよいプロパティの知識について紹介します。

scrollTop

scrollTopはスクロールの一番上から現在のスクロール位置までのピクセル数を保存するプロパティです。
スクロールの開始位置が一番上のスクロールの場合、scrollTopの値は上から下にスクロールされたピクセル数を意味することになります。

scrollTopには任意の値を設定できます。この性質を利用することでスクロールを任意の位置に移動させられます。

画面の拡大縮小を利用した場合、scrollTopは小数になることもあります。

scrollHeight

scrollHeightは画面から見えない部分も含めたスクロール全体の高さを表す読み取り専用のプロパティです。
scrollHeightの高さの定義はclientHeight(後述)と同じで、paddingは含まれますが、境界線・margin・水平スクロールバーは含まれません。

参考: scrollHeightとclientHeightの違いについて

clientHeightとは要素の高さを表す読み取り専用のプロパティです。

scrollHeightは画面から見えない部分も含めたスクロール全体の高さを表すのに対し、clientHeightは画面上に見えているスクロール部分の高さを表します。

スクロールのプロパティに関するサンプルコード

scrollTopclientHeightscrollHeightの具体的な値を示したサンプルコードを紹介します。

<div class="scroller" id="scroller">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<div>clientHeight: <span id="client-height"></span></div>
<div>scrollTop: <span id="scroll-top"></span></div>
<div>scrollHeight: <span id="scroll-height"></span></div>

.scroller {
  overflow: scroll;
  height: 100px;
  padding: 10px;
  margin-bottom: 5px;
  width: 150px;
  border: 5px dashed orange;
}

const scroller = document.getElementById('scroller');

scroller.addEventListener("scroll", event => {
  document.getElementById("scroll-top").textContent = `${scroller.scrollTop}`;
  document.getElementById("client-height").textContent = `${scroller.clientHeight}`;
  document.getElementById("scroll-height").textContent = `${scroller.scrollHeight}`;
});

See the Pen
scroll-properties
by Toshiharu Nishina (@nishina555)
on CodePen.

スクロール位置の操作メソッド

scrollTo

scrollToはスクロールを指定した位置に移動させるメソッドです。

scrollToの引数の指定方法はscrollTo(x-coord, y-coord)scrollTo(options)の2通りがあります。1

x-coordには水平方向の、y-coordには垂直方向のピクセル数を指定します。

optionsleft, top, behaviorをプロパティに持つオブジェクトです。
leftには水平方向の、topには垂直方向のピクセル数を指定します。
behaviorでスクロールのアニメーションが指定できます。behaviorの種類にはsmoothinstantautoがあります。

<div class="scroller" id="scroller">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<div>scrollTop: <span id="scroll-top"></span></div>

.scroller {
  overflow: scroll;
  height: 100px;
  padding: 10px;
  margin-bottom: 5px;
  width: 150px;
  border: 5px dashed orange;
}

const scroller =  document.getElementById('scroller');

// scroller.scrollTo({ top: 300, left: 0, behavior: 'auto' }); と同じ
scroller.scrollTo(0, 300);

document.getElementById("scroll-top").textContent = `${scroller.scrollTop}`;

See the Pen
scroll-to
by Toshiharu Nishina (@nishina555)
on CodePen.

scrollIntoView

scrollIntoViewは要素が見える位置まで親要素をスクロールするメソッドです。

scrollIntoViewは親要素をスクロールするため、スクロール自体にscrollIntoViewを適用してもうまくスクロールが移動しないので注意が必要です。
ですので、scrollIntoViewを利用する際はスクロール要素に子要素を追加し、子要素に対して適用させます。

scrollIntoViewの引数の指定方法はscrollIntoView(scrollIntoViewOptions)scrollIntoView(alignToTop)の2通りがあります。2

scrollIntoViewOptionsinline, block, behaviorをプロパティに持つオブジェクトです。
inlineには水平方向の、blockには垂直方向の配置を指定します。配置の種類にはstart, center, end, nearestがあります。
behaviorでスクロールのアニメーションが指定できます。behaviorの種類にはsmoothautoがあります。

alignToTopには真偽値が指定できます。
truescrollIntoViewOptions: { block: "start", inline: "nearest" }と等価です。
falsescrollIntoViewOptions: { block: "end", inline: "nearest" }と等価です。

<div class='main'>
  <div class="container">
    <strong>block: "center"</strong>
    <div class="scroller">
      <div>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
      </div>
      <div id="target1" class="scroller__target">
         Ut enim ad minim veniam,
      </div>
      <div>
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
      </div>
    </div>
  </div>
  <div class="container">
    <strong>block: "end"</strong>
    <div class="scroller">
      <div>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
      </div>
      <div id="target2" class="scroller__target">
         Ut enim ad minim veniam,
      </div>
      <div>
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
      </div>
    </div>
  </div>
  (略)
</div>
.main {
  display: flex
}

.container:not(:last-child){
  margin-right: 10px;
}

.scroller {
  overflow: scroll;
  height: 100px;
  padding: 10px;
  margin-bottom: 5px;
  width: 150px;
  border: 5px dashed orange;
}

.scroller__target {
  background: lightblue;
}

document.getElementById('target1')
  .scrollIntoView({ block: "center" });
document.getElementById('target2')
  .scrollIntoView({ block: "end" });
document.getElementById('target3')
  .scrollIntoView({ block: "nearest" });
document.getElementById('target4')
  .scrollIntoView(true);
document.getElementById('target5')
  .scrollIntoView({ behavior: "smooth" });

See the Pen
scroll-into-view
by Toshiharu Nishina (@nishina555)
on CodePen.

なお、スクロールのコンテンツ全体を囲んだ要素に対してscrollIntoViewを適用すると、スクロールの位置が指定できます。
以下はscrollIntoView({ block: "center" });を利用してスクロールの初期位置を真ん中の高さにする例です。

<div class="scroller" id="scroller">
  <div id="scroller__inner">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </div>
</div>
<div>clientHeight: <span id="client-height"></span></div>
<div>scrollTop: <span id="scroll-top"></span></div>
<div>scrollHeight: <span id="scroll-height"></span></div>

.scroller {
  overflow: scroll;
  height: 100px;
  padding: 10px;
  margin-bottom: 5px;
  width: 150px;
  border: 5px dashed orange;
}

document.getElementById("scroller__inner")
  .scrollIntoView({ block: "center" });

// 以下はデバッグ用のログ出力
const scroller = document.getElementById('scroller');
document.getElementById("client-height").textContent = `${scroller.clientHeight}`;
document.getElementById("scroll-top").textContent = `${scroller.scrollTop}`;
document.getElementById("scroll-height").textContent = `${scroller.scrollHeight}`;

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

一番下までスクロールされたことを検知する方法

「実際にスクロールした高さ」と「画面上に見えているスクロール画面の高さ」の合計が「スクロール全体の高さ」になった状態は、一番下までスクロールされたことを意味します。
ですので、scrollTop + clientHeight === scrollHeightの真偽値によって一番下までスクロールされたことを検知できます。

scrollTopは小数点を含む可能性のあるプロパティであることを考慮し、Math.abs(scrollHeight - clientHeight - scrollTop) < 1の判定式を利用するとより確実です。

<div class="scroller" id="scroller">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<div>clientHeight: <span id="client-height"></span></div>
<div>scrollTop: <span id="scroll-top"></span></div>
<div>scrollHeight: <span id="scroll-height"></span></div>
<div id="scroll-status">Please read all</div>

.scroller {
  overflow: scroll;
  height: 100px;
  padding: 10px;
  margin-bottom: 5px;
  width: 150px;
  border: 5px dashed orange;
}

#scroll-status {
  width: 150px;
  padding: 5px;
  border: #2A9F00 solid 2px;
}

const scroller = document.getElementById('scroller');

scroller.addEventListener("scroll", event => {
  const clientHeight = scroller.clientHeight;
  const scrollHeight = scroller.scrollHeight;
  const scrollTop = scroller.scrollTop;

  // 一番下までスクロールされたか判定
  if (Math.abs(scrollHeight - clientHeight - scrollTop) < 1) {
    document.getElementById("scroll-status").textContent = `Thank you`;
  }

  // 以下はデバッグ用のログ出力
  document.getElementById("client-height").textContent = `${clientHeight}`;
  document.getElementById("scroll-top").textContent = `${scrollTop}`;
  document.getElementById("scroll-height").textContent = `${scrollHeight}`;
});

See the Pen
scroll-totally-from-top
by Toshiharu Nishina (@nishina555)
on CodePen.

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!