1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ページトップを判定したいだけなのに

Last updated at Posted at 2026-02-02

課題

よくある「トップページに戻る」ボタンが画面固定されている場合について、自分がページトップにいれば非表示(非活性)・いなければ表示(活性)するようにしたい。

問題

もっとも容易に考えられる条件はY方向のスクロール値 window.scrollY が「ゼロか」「ゼロでないか」の時である。

つまりスクロールイベントが発生するたびに window.scrollY === 0 かを判定すればよいのだが、 window.addEventListener(’scroll’, … ) はご存知の通り(?)悪名高く、DebounceやThrottleといったテクニックがないと不要なスクロール処理が過剰発生することで知られている。

ただ、これらの「処理を間引く」ためのテクニックを使った場合、間引いているために少し反応が遅れて見えてしまったり細かいUI上の違和感が現れることもあり、適切でない場面もある印象。

そのため具体的な要素(ヘッダーが隠れた時)などの条件であればIntersection Observerで監視してあげるのがスマートでよいのだが、「ページトップか」を判断するにはそのためだけに見えない1pxの要素をページの一番上に置いて〜などのハックを使うことになるため少し煩わしい。

タイトル通りやりたいのはただ自分がページトップにいるかどうかを判定したいだけなのに……。

解決

アクセスした時点では活性状態にしたくないため、あらかじめ「トップページに戻る」ボタンには inert 属性を付けておく。

<a href="#" id="pagetop" inert>トップページに戻る</a>

hidden (非表示)と違いinert (非活性)属性だけでは非表示にはならないため、別途CSSにて非表示するための記載が必要。

/* CSSアニメーションを付けたい場合は別途transitionなどを使ってください */
#pagetop[inert] { visibility: hidden; }

この前提をもってJavascript上で #pagetop の表示・非表示= inert の真・偽を切り替える。

function pagetopToggle() {
	let ticking = false;
	const pagetop = document.querySelector('#pagetop');

	window.addEventListener('scroll', function() {
		if (!ticking) {
			window.requestAnimationFrame(function() { // 画面のフレームレートに応じて処理を間引く
				const isTop = window.scrollY === 0; // scrollTop: 0ならtrue

				if ( pagetop.inert !== isTop ) { // isTopがinertの有無と一致しなくなった時
					pagetop.inert = isTop; // ページトップ(true)かトップでないか(false)を一致させる
				}
				ticking = false;
			});
			ticking = true;
		}
	}, { passive: true }); // 既存のスクロール処理を邪魔しない=preventDefaultを使用しないことを明示
}

もちろん window.scrollY はスクロールのたびに取得されるが、 inert の付け外し=画面上の変更処理については「ページトップでなくなった」「ページトップに戻った」の2回のみ働くため、パフォーマンス上はIntersection Observerによる監視と同等の軽量さが得られる(はず)。

また、念のため requestAnimationFrame ないし ticking によって画面のフレームレートごとに最低限の「間引き」も行っている。おそらくこれが一番お手軽かも。

※別解:animation-timeline

動くか試せていないのとパフォーマンス的な観点では未知数だが、CSSの animation-timeline が使えるようになればこれだけで済むらしい(すばらしすぎる……)。

ただ、現状ではFirefoxなどにpolyfillが必要なためいずれはJSでCSSをゴニョゴニョする時代も終わるのかもしれないという希望だけ……。

body {
  animation-timeline: scroll();
  animation-range: 0 1px; 
}

#pagetop {
  visibility: hidden; /* content-visibility: none; */
  animation: toggle-pagetop linear both;
}

@keyframes toggle-pagetop {
  to {
    visibility: visible; /* content-visibility: visible; */
  }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?