【JavaScript】scroll – スクロール操作が発生したときにトリガーされるイベント

JavaScriptのscrollイベントは、スクロール操作が発生したときにトリガーされるイベントです。主にページや要素のスクロール位置を取得したり、スクロールに応じた動的な処理を実装する際に利用されます。今回は、scrollイベントの基本的な使い方から、パフォーマンスを考慮した実装方法までをまとめていきたいと思います。

scrollイベントとは?

scrollイベントは、以下の場合に発生します。

  • ユーザーがスクロール操作を行ったとき。
  • JavaScriptでプログラム的にスクロールが発生したとき。

対象となる要素

  • windowオブジェクト
  • スクロール可能なHTML要素(例: <div><section>

注意点として、scrollイベントはバブリング(親要素への伝播)しません。

基本的な使い方

次の例では、ページのスクロール位置を取得して表示します。

See the Pen scroll() by tones (@tonescodedesign) on CodePen.

JavaScript
const indicator = document.getElementById('scrollIndicator');

window.addEventListener('scroll', () => {
  const scrollY = window.scrollY;
  indicator.textContent = `スクロール位置: ${scrollY}px`;
});
HTML
<div class="indicator" id="scrollIndicator">スクロール位置: 0px</div>
CSS
body {
  height: 2000px;
}

.indicator {
  position: fixed;
  top: 10px;
  left: 10px;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 5px 10px;
  border-radius: 5px;
}

このコードでは、window.scrollYを使用してページの垂直方向のスクロール位置を取得しています。

スクロールに応じたアニメーション

スクロール位置に応じて、現在表示されているセクションをナビゲーションでハイライトさせる例です。

See the Pen scroll() by tones (@tonescodedesign) on CodePen.

JavaScript
const navLinks = document.querySelectorAll('nav a');

window.addEventListener('scroll', () => {
  let currentSection = '';
  document.querySelectorAll('section').forEach(section => {
    const sectionTop = section.offsetTop;
    const sectionHeight = section.clientHeight;
    if (window.scrollY >= sectionTop - sectionHeight / 2) {
      currentSection = section.getAttribute('id');
    }
  });

  navLinks.forEach(link => {
    link.classList.remove('active');
    if (link.dataset.target === currentSection) {
      link.classList.add('active');
    }
  });
});
HTML
<nav>
  <a href="#section1" data-target="section1">セクション1</a>
  <a href="#section2" data-target="section2">セクション2</a>
  <a href="#section3" data-target="section3">セクション3</a>
</nav>

<section id="section1" class="section1">セクション1</section>
<section id="section2" class="section2">セクション2</section>
<section id="section3" class="section3">セクション3</section>
CSS
body {
  margin: 0;
}
nav {
  position: fixed;
  top: 0;
  width: 100%;
  background: #333;
  color: #fff;
  padding: 10px;
}
nav a {
  color: #fff;
  margin: 0 10px;
  text-decoration: none;
}
nav a.active {
  text-decoration: underline;
}
section {
  height: 100vh;
  padding: 20px;
}
.section1 { background: lightcoral; }
.section2 { background: lightblue; }
.section3 { background: lightgreen; }

無限スクロールの実装

スクロールに応じて新しいコンテンツをロードする例です。

See the Pen Untitled by tones (@tonescodedesign) on CodePen.

JavaScript
const container = document.getElementById('container');
const loading = document.getElementById('loading');

let isLoading = false;

const loadMoreContent = () => {
  if (isLoading) return;
  isLoading = true;
  loading.style.display = 'block';

  // モックとして、2秒後に新しいコンテンツを追加
  setTimeout(() => {
    for (let i = 0; i < 5; i++) {
      const newItem = document.createElement('div');
      newItem.className = 'content';
      newItem.textContent = `新しいアイテム ${container.children.length + 1}`;
      container.appendChild(newItem);
    }
    loading.style.display = 'none';
    isLoading = false;
  }, 2000);
};

window.addEventListener('scroll', () => {
  if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
    loadMoreContent();
  }
});
HTML
<div id="container">
  <div class="content">アイテム1</div>
  <div class="content">アイテム2</div>
  <div class="content">アイテム3</div>
</div>
<div id="loading">読み込み中...</div>
CSS
.content {
  padding: 10px;
  border-bottom: 1px solid #ddd;
}

#loading {
  text-align: center;
  padding: 20px;
}

使用時の注意点

  • パフォーマンスへの配慮: scrollイベントも頻繁に発生するため、パフォーマンスに影響を与える可能性があります。デバウンスやスロットリングを活用して処理の頻度を制限することをおすすめします。
  • CSSとの組み合わせ: 簡単な視覚効果であれば、CSSのscroll-behaviorposition: stickyを使用することで、JavaScriptを使用せずに実現できる場合があります。
  • イベントリスナーの解除: メモリリークを防ぐため、不要になったイベントリスナーはremoveEventListenerで解除しましょう。

さいごに

scrollイベントは、ページや要素のスクロール操作に反応して動的な処理を実現する便利なイベントです。ただし、頻発するためパフォーマンスに注意が必要です。デバウンスやスロットリングを取り入れることで、実用的で効率的なコードを書くようにしましょう。