●JavaScriptでスクロールイベントを使う基本
Webサイトを閲覧していると、スクロールに合わせてヘッダーが固定されたり、要素がフェードインしたりと、動きのあるページを目にすることがありますよね。
こうした動きを実現するには、JavaScriptのスクロールイベントを活用することが重要です。
○スクロールイベントの発火タイミング
スクロールイベントは、ユーザーがページをスクロールするたびに発火します。
つまり、スクロールが止まるまでは常にイベントが発生し続けるということです。
スクロール量が変化するたびに何らかの処理を実行したい場合に便利ですが、イベントの発火頻度が高すぎると、パフォーマンスに影響を与える可能性もあるので注意が必要ですね。
○スクロール量の取得方法
スクロールイベントが発火したタイミングで、現在のスクロール量を知るにはどうすればいいのでしょうか。
実は、window.pageYOffsetプロパティを使えば、簡単に取得することができます。
このプロパティは、ページの上端からのスクロール量をピクセル単位で返してくれるのです。
○サンプルコード1:スクロール量を表示
それでは、実際にスクロール量を取得して表示するサンプルコードを見てみましょう。
このコードでは、windowオブジェクトのscrollイベントにイベントリスナーを登録しています。
スクロールが発生するたびに、無名関数が呼び出され、window.pageYOffsetプロパティでスクロール量を取得し、console.logで表示しています。
コンソールを開いた状態でページをスクロールすると、リアルタイムでスクロール量が出力されるはずです。
●特定の要素までスクロールしたら処理を実行
Webサイトを作成していると、「特定の要素が画面内に入ったら、アニメーションを開始したい」とか、「ある要素までスクロールしたら、別の要素を表示したい」といったニーズが出てくることがあります。
こうした要件を実現するには、要素の位置を監視して、スクロール位置と比較する必要があります。
○IntersectionObserverを使った実装
この課題を解決する強力な味方が、IntersectionObserverというAPIです。
IntersectionObserverは、ある要素が画面内に入ったかどうかを監視してくれる機能を持っています。
コールバック関数を登録しておけば、要素が画面内に入ったタイミングで自動的に呼び出されるので、煩雑な処理を書かずに済むのが魅力ですね。
IntersectionObserverを使ったコードは、次のようになります。
ここでは、監視対象の要素を#target
としています。
IntersectionObserverのコンストラクタに渡しているコールバック関数内で、isIntersecting
プロパティを確認することで、要素が画面内に入ったかどうかを判定しています。
画面内に入ったときと出たときで異なる処理を実行できるので、柔軟に対応できます。
実際にIntersectionObserverを使ってみると、思っていたよりも簡単に実装できるのに驚くかもしれません。
しかし、IntersectionObserverはIE11では対応していないので、注意が必要です。
IE11をサポートしなければいけない場合は、ポリフィルを使うか、別の方法を検討しましょう。
○getBoundingClientRectを使った実装
IntersectionObserverが使えない環境では、getBoundingClientRectメソッドを使って要素の位置を取得し、スクロール位置と比較する方法があります。
getBoundingClientRectは、要素の位置やサイズを含むオブジェクトを返してくれるメソッドです。
ここでは、getBoundingClientRectを使って特定の要素までスクロールしたかどうかを判定するコードを見てみましょう。
getBoundingClientRect
メソッドで取得した要素の位置情報のうち、top
プロパティに注目しています。
これは、要素の上端から画面の上端までの距離を表しています。
一方、window.innerHeight
は画面の高さを表します。
したがって、targetRect.top <= window.innerHeight
という条件は、「要素の上端が画面の下端より上に来た」というタイミングを表しています。
つまり、要素が画面内に入ったということですね。
この方法は、IntersectionObserverと比べると少し複雑で、パフォーマンスの面でも劣ります。
しかし、IE11でも動作するので、幅広い環境で使えるのがメリットです。
○サンプルコード2:特定要素までスクロールでクラス付与
それでは、特定の要素までスクロールしたら、その要素にクラスを付与するサンプルコードを見てみましょう。
ここでは、IntersectionObserverを使った実装にしています。
#target
要素が画面内に入ったら、active
クラスを付与しています。
これにより、CSSで背景色を黄色に設定しているので、要素が画面内に入るとハイライト表示されるようになります。
画面外に出ると、active
クラスが外れて、元の表示に戻ります。
このように、IntersectionObserverを使えば、監視対象の要素が画面内に入ったかどうかに応じて、柔軟にクラスの付与や削除ができます。
アニメーションを適用したい場合も、クラスを切り替えるだけなので、シンプルに実装できるのが嬉しいポイントですね。
○サンプルコード3:特定要素までスクロールで要素を非表示
続いて、特定の要素までスクロールしたら、別の要素を非表示にするサンプルコードを紹介します。
ここでは、#target
要素が画面内に入ったら、#hidden
要素を非表示にしています。
非表示にする際は、opacity
プロパティを使って透明度を0にしつつ、pointer-events
プロパティで要素へのマウスイベントを無効化しています。
これにより、要素が非表示になるだけでなく、クリックもできなくなります。
また、transition
プロパティを使って、opacity
の変化にアニメーションを付けています。
これにより、要素がゆっくりとフェードアウトしていくような効果が得られます。
実際に実装してみると、画面内に入ったら特定の要素をスムーズに消していく演出が、とてもシンプルなコードで実現できるのに感動しますよ。
IntersectionObserverの使い方を覚えておくと、こうしたスクロールに連動した処理が手軽に実装できるので、ぜひマスターしておきたいテクニックです。
●スクロール位置の取得と指定
JavaScriptでスクロール操作を行うとき、現在のスクロール位置を知ることや、任意の位置にスクロールさせることは非常によくあるニーズです。
例えば、「トップに戻る」ボタンを実装するときは、現在のスクロール位置に応じてボタンの表示/非表示を切り替えたり、ボタンをクリックしたら一番上までスクロールさせたりといった処理が必要になります。
○scrollTopを使ったスクロール位置の取得
現在のスクロール位置を取得するには、scrollTop
プロパティを使います。
これは、要素の上端から、その要素の表示領域の上端までの距離を表します。
window
オブジェクトのscrollTop
プロパティを参照すれば、ページ全体のスクロール位置を取得できます。
ここでは、window.scrollTop
とdocument.documentElement.scrollTop
の両方を確認しています。
これは、ブラウザによってscrollTop
プロパティの実装が異なるためです。
モダンなブラウザではwindow.scrollTop
で取得できますが、古いIEではdocument.documentElement.scrollTop
を使う必要があります。
scrollTop
の値は、スクロールが一番上のときは0で、下にスクロールするほど大きくなります。
例えば、100pxスクロールした位置ではscrollTop
は100になります。
○scrollToを使ったスクロール位置の指定
一方、任意の位置にスクロールさせるには、scrollTo
メソッドを使います。
これは、指定した座標までスクロールさせるメソッドで、window
オブジェクトに対して使います。
この例では、横方向のスクロール位置を0、縦方向のスクロール位置を500に指定しています。
つまり、ページの一番上から500px下にスクロールさせるという意味です。
scrollTo
メソッドには、オプションでスクロールの動作を指定することもできます。
このように、behavior
プロパティに'smooth'
を指定すると、スムーズにスクロールするようになります。
一気にスクロールするのではなく、ゆっくりとアニメーションしながら目的の位置まで移動するので、ユーザー体験の向上につながります。
○サンプルコード4:ボタンクリックで指定位置にスクロール
それでは、「トップに戻る」ボタンの実装例を見てみましょう。
ここでは、id="scroll-to-top"
の<button>
要素を用意し、それをクリックしたときの処理をaddEventListener
で登録しています。
window.scrollTo
メソッドを呼び出して、top
を0に、behavior
を'smooth'
に指定することで、一番上までスムーズにスクロールさせています。
実際に動かしてみると、「トップに戻る」ボタンをクリックすると、画面が上に向かってゆっくりとスクロールしていく様子が確認できるはずです。
こうした機能は、ページ内の移動を快適にするのに役立ちます。
●ページ最下部・最上部の判定
Webサイトを作成していると、「ページの最下部まで達したら、新しいコンテンツを追加読み込みしたい」とか、「最上部に戻ったら、ナビゲーションメニューのデザインを変更したい」といったニーズが出てくることがあります。
こうした要件を実現するには、ページの最下部や最上部に達したかどうかを判定する必要があります。
○scrollHeightとclientHeightを使った最下部判定
ページの最下部に達したかどうかを判定するには、scrollHeight
とclientHeight
という2つのプロパティを使います。
scrollHeight
は、スクロールされる要素の全体の高さを表し、clientHeight
は、表示領域の高さを表します。
つまり、scrollHeight - clientHeight
は、スクロールされる要素のうち、表示領域に収まらない部分の高さを表します。
これと現在のスクロール位置であるscrollTop
を比較することで、最下部に達したかどうかを判定できます。
ここでは、document.documentElement
のscrollHeight
とclientHeight
を取得し、window.scrollTop
またはdocument.documentElement.scrollTop
でスクロール位置を取得しています。
そして、scrollTop + clientHeight
がscrollHeight
以上であれば、最下部に達したと判定しています。
この判定方法は、ページ全体がスクロールの対象になっている場合に有効です。
特定の要素内でスクロールしている場合は、その要素のscrollHeight
とclientHeight
を使うように注意しましょう。
○scrollTopを使った最上部判定
一方、ページの最上部に達したかどうかを判定するのは、もっと簡単です。
scrollTop
が0であれば、最上部にいるということになります。
ここでも、window.scrollTop
とdocument.documentElement.scrollTop
の両方を確認して、スクロール位置を取得しています。
scrollTop
が0であれば、最上部に達したと判定できます。
最上部の判定は、ページ全体がスクロールの対象でも、特定の要素内でスクロールしている場合でも、同じ方法で行えます。
要素内でスクロールしている場合は、その要素のscrollTop
を確認すればいいですね。
○サンプルコード5:最下部判定でローディング表示
それでは、ページの最下部に達したら、新しいコンテンツを追加読み込みする実装例を見てみましょう。
ここでは、#content
要素にコンテンツを表示し、#loading
要素でローディング中の表示を行っています。
#loading
要素は、初期状態ではdisplay: none;
で非表示にしておきます。
window
のscroll
イベントを監視し、最下部に達したかどうかを判定しています。
最下部に達したら、#loading
要素のdisplay
スタイルを'block'
に変更して、ローディング中の表示を行います。
そして、新しいコンテンツを取得する処理を行います。
この部分は、APIを呼び出したりAjaxで通信したりと、状況に応じて実装が異なるので、ここでは詳細を省略しています。
新しいコンテンツの取得が完了したら、再び#loading
要素を非表示にしましょう。
こうすることで、ユーザーがページの最下部までスクロールしたときに、自動的に新しいコンテンツが追加されるようになります。
SNSの投稿一覧などで見かける、無限スクロール機能の基本的な仕組みです。
実際に実装してみると、最下部の判定はそれほど難しくないことがわかると思います。
ページの最上部や最下部に達したタイミングで何らかの処理を行いたい場合は、ぜひこの判定方法を活用してみてください。
●スムーススクロールの実装
Webサイトを閲覧していると、ページ内リンクをクリックしたときに、目的の位置までスムーズにスクロールするサイトを見かけることがあります。
こうしたスムーススクロールの動きがあると、ページ内の移動がスムーズで快適に感じられ、ユーザー体験の向上につながります。
○CSS scroll-behaviorプロパティ
実は、スムーススクロールを実装するのに、JavaScriptを使わなくても済む方法があります。
それが、CSS の scroll-behavior
プロパティです。
このプロパティを使えば、シンプルにスムーススクロールを実現できます。
このように、html
要素に scroll-behavior: smooth;
を指定するだけで、ページ内リンクをクリックしたときのスクロールがスムーズになります。
scroll-behavior
プロパティには、auto
(デフォルト)と smooth
の2つの値があり、smooth
を指定することでスムーススクロールが有効になるのです。
ただし、scroll-behavior
プロパティはまだ比較的新しい機能で、すべてのブラウザで対応しているわけではありません。
特に、IE11では使えないので注意が必要です。
幅広いブラウザ対応が必要な場合は、次に紹介するJavaScriptを使った方法を検討しましょう。
○JavaScriptでスムーススクロールを実装
JavaScriptを使ってスムーススクロールを実装するには、scrollTo
メソッドを使います。
先ほど紹介した scroll-behavior
プロパティは、ページ内リンクをクリックしたときのスクロールにしか効果がありませんが、JavaScriptを使えば、任意のタイミングでスムーススクロールを発動させることができます。
スムーススクロールを実装するJavaScriptのコードは、次のようになります。
ここでは、scrollToSmooth
という関数を定義しています。
この関数は、スクロール先の位置を引数 pos
で受け取り、window.scrollTo
メソッドを呼び出してスクロールを実行します。
scrollTo
メソッドのオプションで、behavior: 'smooth'
を指定することで、スムーススクロールが有効になります。
あとは、この scrollToSmooth
関数を任意のタイミングで呼び出せば、スムーススクロールが実行されます。
例えば、ボタンのクリックイベントに合わせて呼び出すことで、ボタンクリックでスムーススクロールを発動させることができますね。
○サンプルコード6:ページ内リンクのスムーススクロール
それでは、ページ内リンクをクリックしたときにスムーススクロールを実行するサンプルコードを見てみましょう。
ここでは、a
要素の href
属性が #
で始まるものを選択し、それぞれにクリックイベントを登録しています。
クリックされたら、デフォルトの動作をキャンセルし、href
属性で指定された要素の位置を取得します。
そして、その位置までスムーススクロールするように、window.scrollTo
メソッドを呼び出しています。
このコードを使えば、ページ内リンクをクリックしたときに、目的の位置までスムーズにスクロールするようになります。
scroll-behavior
プロパティが使えない環境でも、JavaScriptでスムーススクロールを実現できるので便利ですね。
○サンプルコード7:慣性スクロールの実装
スムーススクロールをさらに快適にするテクニックとして、慣性スクロールがあります。
慣性スクロールとは、スクロールを止めたあとも、しばらくの間はスクロールが続くような効果のことです。
まるで、物理的な慣性で動き続けているかのように見えるため、このように呼ばれています。
慣性スクロールを実装するには、少し複雑なコードが必要になります。
ここでは、scrollToSmooth
関数の中で、requestAnimationFrame
を使ってアニメーションを実装しています。
easeInOutCubic
という関数は、イージング関数の一種で、スクロールの速度に変化を付けるために使っています。
慣性スクロールを使うと、スクロールの終了がより自然な感じになります。
通常のスムーススクロールだと、目的の位置に到達した瞬間に止まってしまいますが、慣性スクロールだと、少しオーバーシュートしてから徐々に止まるような動きになります。
ただし、慣性スクロールの実装は少し複雑で、イージング関数の扱いにも慣れが必要です。
また、パフォーマンスへの影響も考慮しなければいけません。
必ずしも慣性スクロールが必要というわけではないので、状況に応じて使い分けましょう。
スムーススクロールの実装方法については以上です。
CSS だけで簡単に実装する方法と、JavaScript を使ってより柔軟に実装する方法を紹介しました。
スムーススクロールを取り入れることで、Webサイトのユーザー体験を向上させることができます。
ぜひ、自分のサイトに取り入れてみてください。
●よくあるエラーと対処法
JavaScriptでスクロール関連の機能を実装していると、思うように動かなくてハマってしまうことがありますよね。
特に、初心者の頃は、エラーが出ても原因がわからず、試行錯誤を繰り返した経験があるのではないでしょうか。
ここでは、スクロール関連の実装でよく遭遇するエラーを取り上げ、その原因と対処法を解説します。
これらのエラーに悩まされている方は多いと思うので、ぜひ参考にしてみてください。
○scrollイベントが発火しない場合の対処法
まず、scroll
イベントが発火しない場合の対処法です。
scroll
イベントを使って、スクロール時の処理を実装しようとしたのに、全然動かない…というのは、よくある悩みです。
scroll
イベントが発火しない原因は、大きく分けて2つあります。
1つ目は、scroll
イベントを設定する要素が間違っているケースです。
scroll
イベントは、スクロール可能な要素に対して設定する必要があります。
よくあるミスが、document
やbody
要素にscroll
イベントを設定しようとすることです。
この要素はスクロールできないので、scroll
イベントは発火しません。
正しくは、window
オブジェクトか、スクロール可能な要素(overflow
プロパティがauto
やscroll
に設定されている要素)に対して、scroll
イベントを設定します。
2つ目は、scroll
イベントを設定するタイミングが遅すぎるケースです。
よくあるのが、scroll
イベントを設定するコードを</body>
タグの直前に書くことです。
この場合、コードが実行されるタイミングでは、すでにスクロールが発生している可能性があります。
これを防ぐには、scroll
イベントを設定するコードを<head>
タグ内か、<body>
タグの直後に書きます。
あるいは、DOMContentLoaded
イベントを使って、DOM構築が完了したタイミングでscroll
イベントを設定するのも良いでしょう。
これらの点に気をつけることで、scroll
イベントが発火しない問題を解決できます。
○スムーススクロールが効かない場合の対処法
続いて、スムーススクロールが効かない場合の対処法です。
せっかくJavaScriptでスムーススクロールを実装したのに、全然スムーズにスクロールしてくれない…という経験をしたことがある方も多いのではないでしょうか。
スムーススクロールが効かない原因は、主に2つあります。
1つ目は、スムーススクロールを実装するコードが間違っているケースです。
特に、scrollTo
メソッドのオプションであるbehavior: 'smooth'
を指定し忘れることが多いです。
behavior: 'smooth'
を指定しないと、通常のスクロールになってしまいます。
2つ目は、ブラウザがスムーススクロールに対応していないケースです。
scrollTo
メソッドのbehavior: 'smooth'
オプションは、比較的新しい機能で、すべてのブラウザで対応しているわけではありません。
特に、IE11では使えません。
これを防ぐには、scroll-behavior
プロパティを使ったCSSでのスムーススクロール実装を検討するか、JavaScriptでスムーススクロールを実装する際に、ブラウザ対応を確認するようにします。
こうすることで、ブラウザ対応の問題によるスムーススクロールの動作不良を回避できます。
○横スクロールバーが表示されない場合の対処法
最後に、横スクロールバーが表示されない場合の対処法です。
要素の横幅が画面からはみ出しているのに、横スクロールバーが表示されず、はみ出した部分が見えない…というのは、レイアウトを調整する際によく遭遇する問題ですね。
横スクロールバーが表示されない原因は、要素や親要素のoverflow-x
プロパティがvisible
(デフォルト値)になっているためです。
overflow-x
プロパティは、要素の横幅がはみ出した場合の処理を指定するプロパティで、visible
の場合は、はみ出した部分が見えなくなります。
これを防ぐには、はみ出す可能性のある要素やその親要素にoverflow-x: auto;
を指定します。
overflow-x: auto;
を指定すると、要素の横幅がはみ出した場合に、自動的に横スクロールバーが表示されるようになります。
また、はみ出す可能性のある要素にwidth
プロパティを指定して、明示的に横幅を設定するのも良いでしょう。
このように、overflow-x
プロパティとwidth
プロパティを適切に設定することで、横スクロールバーが表示されない問題を解決できます。
●スクロール関連の応用例
これまで、スクロールイベントの基本的な使い方や、スクロール位置の取得・指定、スムーススクロールの実装方法などを解説してきました。
上述した知識を組み合わせるだけで、Webサイトのユーザー体験を向上させる様々な機能を実装することができます。
ここでは、スクロール関連のテクニックを応用した実装例を3つ紹介します。
実際のWebサイトでよく見かける機能ばかりなので、ぜひ参考にしてみてくださいね。
○サンプルコード8:スクロールでヘッダーを固定・解除
1つ目は、スクロールに合わせてヘッダーを固定・解除する機能です。
Webサイトを閲覧していると、ページの上部にあるヘッダーが、スクロールすると画面上部に固定されるようになるサイトを見かけますよね。
これは、ユーザーがページ内を移動しやすくするための工夫の1つです。
この機能は、スクロール位置を監視して、一定量スクロールしたらヘッダーにposition: fixed;
を指定することで実装できます。
ここでは、ヘッダー要素の高さ(headerHeight
)を取得しておき、スクロール位置(window.pageYOffset
)がヘッダーの高さ以上になったら、ヘッダーにposition: fixed;
とtop: 0;
を指定して、画面上部に固定しています。
スクロール位置がヘッダーの高さ未満になったら、position
とtop
のスタイルを解除して、元の位置に戻すようにしています。
このように、スクロール位置を監視することで、スクロールに合わせてヘッダーの表示を切り替えることができます。
ヘッダーメニューが常に表示されていると、ページ内の移動がスムーズになるので、ユーザビリティの向上につながりますね。
○サンプルコード9:スクロール連動のパララックス効果
2つ目は、スクロールに連動したパララックス効果の実装です。
パララックス効果とは、背景とコンテンツで異なるスクロール速度を設定することで、奥行きのある表現を演出する手法です。
Webサイトのデザインにアクセントを付けるのに効果的ですよ。
パララックス効果は、スクロール位置に応じて背景の位置をずらすことで実装できます。
具体的には、background-position
プロパティを使って、背景画像の位置を調整します。
ここでは、#parallax
要素に背景画像を指定し、background-attachment: fixed;
で背景画像を固定しています。
そして、スクロール位置(window.pageYOffset
)に応じて、background-position
プロパティのY座標を調整しています。
scrollPosition * 0.5
としているのがポイントで、スクロール位置の半分の速度で背景画像を移動させることで、パララックス効果を演出しています。
0.5
の値を変更することで、パララックス効果の強弱を調整できます。
このように、スクロール位置に応じて背景画像の位置をずらすことで、奥行きのある表現を実現できます。
Webサイトのデザインに動きを加えることで、ユーザーの印象に残りやすくなりますよ。
○サンプルコード10:無限スクロールの実装
3つ目は、無限スクロールの実装です。
無限スクロールとは、ページの最下部までスクロールすると、自動的に次のコンテンツを読み込んで表示する機能のことです。
SNSのタイムラインなどでよく見かける機能ですね。
無限スクロールは、ページの最下部までスクロールしたかどうかを判定し、最下部に達したらAjaxで次のコンテンツを取得して表示することで実装できます。
ここでは、window
のscroll
イベントを監視し、スクロール位置(window.innerHeight + window.pageYOffset
)がしきい値(document.documentElement.offsetHeight - 100
)以上になったら、次のコンテンツを取得して表示するようにしています。
しきい値は、ドキュメントの高さから任意の値(ここでは100px)を引いた値にしています。
これは、コンテンツの最下部に到達する少し手前でコンテンツを読み込み始めるようにするためです。
次のコンテンツを取得して表示する処理は、APIを呼び出してデータを取得し、取得したデータをもとにコンテンツを生成して#content
要素に追加する、といった流れになります。
具体的な実装は、APIの仕様やデータの構造によって異なるので、ここでは割愛します。
無限スクロールを実装することで、ユーザーは次々とコンテンツを閲覧できるようになり、ページ内の回遊率を高めることができます。
ただし、大量のコンテンツを一度に読み込むと、パフォーマンスが低下する可能性があるので、適切な量のコンテンツを読み込むようにしましょう。
まとめ
本記事では、JavaScriptを使ってスクロールに関連する様々な機能を実装する方法を紹介してきました。
スクロールイベントの基本的な使い方から、特定の要素までスクロールしたときの処理、スクロール位置の取得と指定、ページの最下部・最上部の判定、スムーススクロールの実装など、スクロールに関するテクニックを幅広く取り上げました。
また、実装する際によくつまずくポイントとその対処法も解説し、より実践的な知識が身につくように心がけました。
さらに、ヘッダーの固定・解除やパララックス効果、無限スクロールなど、スクロール関連のテクニックを応用した実装例も紹介しました。
本記事で紹介した内容を理解することで、スクロールを活用したより魅力的なWebサイトを制作できるようになるでしょう。
スクロールは、ユーザーとWebサイトのインタラクションを豊かにする重要な要素です。
ユーザーがページをスクロールしたときに、どのような体験を提供できるかを考えながら、スクロールを活用したWebサイト作りを楽しんでいただければと思います。