Intersection Observer APIを用いた「ページトップに戻るリンク」の実装

はじめに

このブログはWordPressを利用していますが、テーマは自作のものを活用しています。自作なので、最初から完成されたものではなく、時間があるときに、少しずつ自分が理想とする実装に近づけている状況です。最近の個人的なマイルストーンは、jQueryフリーになったことでしょうか。元々、jQueryの理念として、JavaScript等を用いたHTML要素やCSSの操作に対して、各ブラウザ間の挙動の違いを吸収するというものがあったと思いますが、JavaScriptも進化して、またCSSも標準化が進み、昔はjQueryを使わないと冗長になりがちだったスクリプト記載が、素のJavaScript(いわゆるVanilla JavaScript)で、比較的簡単に記述できるようになりました。いい時代になったものです。

実装したいこと

さて、Vanilla JSを用いて、以下の実装をしてみたいなと思いました。

  • ページ右下に「ページトップに戻るリンク」を付ける(位置は固定)。
  • そのリンクは、最初は表示されていなくて、ある程度下にスクロールしたら表示される。
  • クリックすると、スムーズスクロールにより、スルスルとページトップに戻り、そのリンクは表示されなくなる(以下、繰り返し)。

以前であれば、DOM要素に対して、その相対位置やscroll量を検出して…とそれなりに手の込んだ実装にする必要があったと思いますが(それをjQueryは簡潔に記載出来たのですが)、今は、Intersection Observer API というのがあり、そのAPIを使えば、特定の領域を監視して、指定したDOM要素がその領域に入ったかどうかを検出してくれます。当然ながらモダンなブラウザは対応してます。これを使えば、上記のやりたいことが実現できそうです。

実際の記述

キモとなるスクリプトは、以下の記載で済みます。たったの3行です。シンプルでいいですね。

(new IntersectionObserver(items =>{
	document.querySelector('#ToPageTop').style.opacity = 1 - Number(items[0].isIntersecting);
})).observe(document.querySelector('#header'));

ここでは、監視する領域は、ビューポート~ブラウザの画面で、その領域で監視する対象は、document.querySelector('#header')で規定されるヘッダー領域です。ページを下にスクロールしていくと、ヘッダー領域は、ビューポート上部から上に進んで、徐々に見えなくなります。ヘッダー領域が見えているうちは、items[0].isIntersectingで得られる論理値はtrueですが、ヘッダー領域が見切れてしまうと、items[0].isIntersectingは、falseを返します。
これを右下に固定されたページトップに戻るためのリンク(ここでは、document.querySelector('#ToPageTop'))の透明度に反映させれば、ヘッダー領域の見え隠れによって、リンクが現れたり消えたりするのが実現できるという訳です。透明度は、opacityで指定してますが、じわっと消えたり現れたりというのをみせるために、CSSのtransitionを使用しています。

#ToPageTop {
  position: fixed;
  bottom: 1.5rem;
  right: 2vw;
  cursor: pointer;
  z-index: 99;
  transition: opacity 1s ease-out; /* この部分でプチ・アニメーション */
}

おまけ

実際に試すと分かると思いますが、「ページトップに戻るリンク」をクリックしてこちらが意図する動きは、瞬時にページトップに戻るのではなく、スルスルとスクロールしてページ上部まで移動するような動作です。最初は、window.scrollToなどを用いてやろうと思っていましたが、まぁそこまではいいかなと現状は以下の指定となっています。目次でのリンクをクリックしても、その対象先までスルスルと行くような仕様ですが、こちらもシンプルですね。これに関しては、また別のエントリーで解説しようと思います。

document.querySelectorAll('a[href^="#"]').forEach(item => {
	item.addEventListener('click', (e) => {
		e.preventDefault();
		document.querySelector(item.hash).scrollIntoView({behavior: "smooth", block: "start"});
	})
});

Vanilla JSに関しては、CSSの擬似クラス等とJSのquerySelector等を組合せて、DOM操作系が随分と楽になりましたね。いい時代になったものです(2回目)。

■ 了

|