スクロール位置の取得とは何か
スクロール位置の取得は「今、ページや要素がどれだけスクロールされているか」を数値で読み取ることです。縦方向はどれだけ上から下へ動いたか(Top)、横方向は左から右へ動いたか(Left)を表します。ここが重要です:ページ全体と特定要素で取得方法が違い、さらに“画面に対する位置”を知りたい場合は別の取り方(rect)になります。目的に応じた正しい API を選ぶのがコツです。
ページ全体のスクロール位置を読む
基本(window.scrollY / scrollX)
<div style="height:2000px"></div>
<script>
window.addEventListener("scroll", () => {
console.log("縦:", window.scrollY, "横:", window.scrollX);
}, { passive: true });
</script>
HTMLここが重要です:window.scrollY/scrollX は「ビューポート上端がページ左上からどれだけ離れたか」を直接返します。最もシンプルで、現代ブラウザで広く使えます。
互換的な代替(pageYOffset / scrollingElement.scrollTop)
<script>
const y = window.pageYOffset; // scrollY と同義
const x = window.pageXOffset; // scrollX と同義
// より汎用的に取りたいなら
const root = document.scrollingElement || document.documentElement;
const y2 = root.scrollTop;
const x2 = root.scrollLeft;
</script>
HTMLここが重要です:古い環境や特殊モードでは document.scrollingElement(通常は html)で読むのが堅牢です。pageYOffset も scrollY と同等に使えます。
特定の要素のスクロール位置を読む
要素内スクロール(element.scrollTop / scrollLeft)
<div id="panel" style="height:160px; overflow:auto; border:1px solid #ccc">
<div style="height:800px"></div>
</div>
<script>
panel.addEventListener("scroll", () => {
console.log("縦:", panel.scrollTop, "横:", panel.scrollLeft);
}, { passive: true });
</script>
HTMLここが重要です:scrollTop/scrollLeft は“その要素の内側スクロール”の現在位置です。最大値は「必要量 − 見えている量」で決まり、縦なら scrollHeight − clientHeight、横なら scrollWidth − clientWidth が上限になります。
スクロール可能量と端の判定
<script>
function canScrollDown(el) {
return el.scrollTop < el.scrollHeight - el.clientHeight;
}
function canScrollRight(el) {
return el.scrollLeft < el.scrollWidth - el.clientWidth;
}
</script>
HTMLここが重要です:端で無理にスクロールしても動かないため、事前に「まだ進めるか」を判定してボタン状態などを同期すると親切です。
画面に対する位置を知る(rect とスクロール併用)
要素の“画面座標”と“ページ座標”
<script>
const r = target.getBoundingClientRect(); // 画面に対する位置とサイズ(小数あり)
const pageTop = window.scrollY + r.top; // ページ左上からの絶対位置
const pageLeft = window.scrollX + r.left;
</script>
HTMLここが重要です:getBoundingClientRect() は“ビューポート”基準です。ページの絶対座標が欲しい時は scrollY/scrollX を足し込みます。固定ヘッダの重なり補正もこの組み合わせで行います。
実践例(進捗バー、下端判定、スクロール方向検知、保存と復元)
ページ進捗バーの更新
<div id="bar" style="position:fixed;top:0;left:0;height:3px;background:#09f;width:0"></div>
<script>
function updateBar() {
const h = document.documentElement.scrollHeight;
const vh = document.documentElement.clientHeight;
const max = Math.max(h - vh, 1);
const w = Math.min(window.scrollY / max, 1) * 100;
bar.style.width = w + "%";
}
window.addEventListener("scroll", () => requestAnimationFrame(updateBar), { passive: true });
updateBar();
</script>
HTMLここが重要です:“最大量 − 見えている量”で正しく割り、rAF 内で描画更新すると滑らかで正確な進捗になります。
無限スクロールの下端近接判定
<script>
function nearBottom(px = 200) {
const root = document.scrollingElement || document.documentElement;
return root.scrollTop + root.clientHeight > root.scrollHeight - px;
}
window.addEventListener("scroll", () => {
if (nearBottom()) {
// 追加読み込み処理
}
}, { passive: true });
</script>
HTMLここが重要です:clientHeight(見えている高さ)と scrollHeight(必要高さ)で“あとどれくらいか”を判定します。余白 px を設けると体験が自然になります。
スクロール方向の検知(上向き/下向き)
<script>
let lastY = window.scrollY;
function onScroll() {
const y = window.scrollY;
const dir = y > lastY ? "down" : y < lastY ? "up" : "none";
lastY = y;
console.log("方向:", dir);
}
window.addEventListener("scroll", onScroll, { passive: true });
</script>
HTMLここが重要です:前回値と比較するだけで方向が判断できます。ヘッダ表示/非表示の切替などに活用できます。
スクロール位置の保存と復元
<script>
// 保存
window.addEventListener("scroll", () => {
sessionStorage.setItem("scrollY", String(window.scrollY));
}, { passive: true });
// 復元
window.addEventListener("load", () => {
const y = Number(sessionStorage.getItem("scrollY") || 0);
window.scrollTo({ top: y, behavior: "instant" });
});
</script>
HTMLここが重要です:ページ再表示時に位置を戻すだけで再学習の負担を減らせます。復元はレイアウト確定後(load)に行うとズレが起きにくいです。
設計の勘所(どの値を使うか、性能、アクセシビリティ)
値の選び分けを明確にする
“ページ全体”は window.scrollY/scrollX(または scrollingElement.scrollTop/Left)、“要素内”は element.scrollTop/Left、“画面に対しての位置”は rect(+scrollY/scrollXで絶対座標へ)。ここが重要です:何を基準にしたいか(ページか要素か画面か)を先に決めると、迷いとズレが減ります。
性能を守る
スクロールは高頻度で発火します。処理は軽くし、描画更新は requestAnimationFrame にまとめ、監視は { passive: true } を付けます。ここが重要です:間引き(throttle)や状態の単一情報源(進捗バーなどは1箇所で更新)を徹底すると体感が安定します。
アクセシビリティに配慮する
スクロール後に目的要素へ focus() を当てるとキーボード操作にも優しくなります。固定ヘッダがある場合はヘッダ分を差し引いて停止位置を設計し、視差やアニメ軽減設定が有効な環境ではスムーズスクロールを避ける選択肢も検討します。ここが重要です:見え方だけでなく操作性も合わせると、使いやすさが大きく向上します。
よくある落とし穴と回避策
固定ヘッダの重なりで“目的地が隠れる”問題は、rect.top に scrollY を足し、ヘッダ高を差し引いて座標を求めることで解消できます。ページと要素の値を混同するとズレるため、window.scrollY と element.scrollTop の“基準の違い”を常に意識します。scrollHeight/clientHeight の“最大量 − 見えている量”の引き忘れは進捗が 100% に届かない典型エラーです。さらに、display: none の要素はレイアウト計算されず値が 0 になるため、計測時は visibility: hidden など“レイアウト参加状態”で測るのが安全です。
まとめ
スクロール位置は、ページなら window.scrollY/scrollX(または scrollingElement.scrollTop/Left)、要素なら element.scrollTop/Left、画面基準なら rect(+scrollY/scrollX)で取得します。進捗や端判定は scrollHeight/clientHeight(横は scrollWidth/clientWidth)と組み合わせるのが鉄則。固定ヘッダの補正や方向検知、保存・復元まで整えると、滑らかで正確なスクロール体験が作れます。処理は軽く、rAF と passive を活用し、基準の選択を明確にすれば、初心者でもズレのない堅実な実装ができます。
