備忘録-スクロール禁止とアンカーリンクで動的なUIを作成する方法-スマホ向けメニュー

CSS、JavaScript

モバイルファーストの時代では、スマートフォン向けのメニューの使いやすさが、ユーザー体験(UX)の向上に大きく貢献します。この記事では、スマートフォンメニューを作成する際に必要な、スクロール禁止アンカーリンクでメニューが閉じる機能の実装方法を、初心者でもわかりやすく解説します。

モバイルメニューの基本構造

まず、シンプルなモバイルメニューをHTMLで作成します。メニューを開くボタンとメニュー内容を含め、リンクで指定した場所にスクロールする基本的な構造です。

<!-- メニューを開くボタン -->
<button class="menu-button">メニューを開く</button>

<!-- メニュー本体 -->
<div class="menu">
  <button class="close-button">× 閉じる</button>
  <nav>
    <ul>
      <li><a href="#contentA" class="menu-link">コンテンツA</a></li>
      <li><a href="#contentB" class="menu-link">コンテンツB</a></li>
      <li><a href="#contentC" class="menu-link">コンテンツC</a></li>
      <li><a href="#contentD" class="menu-link">コンテンツD</a></li>
    </ul>
  </nav>
</div>
  • .menu-buttonボタンを押すとメニューが表示され、メニュー内のリンクをクリックするとページ内でスムーズにスクロールします。
  • href="#contentA"のように、リンクをアンカーリンクにして、指定した位置にスクロールできます。

CSSでメニューのデザイン

次に、メニューを画面全体に表示させるためのCSSを追加します。モバイル画面にぴったりのデザインを適用します。

body {
  font-family: Arial, sans-serif;
}

.menu-button {
  position: fixed;
  top: 20px;
  left: 20px;
  padding: 10px 20px;
  background: #333;
  color: #fff;
  border: none;
  cursor: pointer;
}

.menu {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  transform: translateY(-100%);
  transition: transform 0.3s ease-in-out;
}

.menu.open {
  transform: translateY(0);
}

.close-button {
  background: red;
  color: white;
  border: none;
  padding: 10px;
  margin-bottom: 20px;
  cursor: pointer;
}

.menu nav ul {
  list-style: none;
  padding: 0;
  text-align: center;
}

.menu nav ul li {
  margin: 15px 0;
}

.menu nav ul li a {
  color: white;
  text-decoration: none;
  font-size: 1.5rem;
}
  • メニューの高さを100%に設定し、画面全体をカバーするデザインにしています。
  • transform: translateY(-100%);を使って初期状態ではメニューを隠し、transform: translateY(0);でメニューが表示されるようにします。

JavaScriptでスクロール禁止とメニューの開閉を制御

ここでは、メニューを開いた時にスクロールを完全に停止し、メニュー内のリンクをクリックするとメニューが閉じるようにします。これを実現するためには、JavaScriptでスクロール禁止とイベント制御を行います。

const menu = document.querySelector('.menu');
const menuButton = document.querySelector('.menu-button');
const closeButton = document.querySelector('.close-button');
const menuLinks = document.querySelectorAll('.menu-link');

let scrollPosition = 0; // スクロール位置を保存する変数

function disableScroll() {
  scrollPosition = window.scrollY; // 現在のスクロール位置を保存
  document.body.style.overflow = 'hidden';
  document.body.style.position = 'fixed';
  document.body.style.width = '100%';
  document.body.style.height = '100%';
  document.body.style.top = `-${scrollPosition}px`;

  document.addEventListener('touchmove', preventDefault, { passive: false });
  document.addEventListener('wheel', preventDefault, { passive: false });
}

function enableScroll() {
  document.body.style.removeProperty('overflow');
  document.body.style.removeProperty('position');
  document.body.style.removeProperty('width');
  document.body.style.removeProperty('height');
  document.body.style.removeProperty('top');
  window.scrollTo(0, scrollPosition); // 保存したスクロール位置に戻す

  document.removeEventListener('touchmove', preventDefault);
  document.removeEventListener('wheel', preventDefault);
}

function preventDefault(event) {
  event.preventDefault();
}

menuButton.addEventListener('click', () => {
  menu.classList.add('open');
  disableScroll();
});

closeButton.addEventListener('click', () => {
  menu.classList.remove('open');
  enableScroll();
});

// メニュー内のリンクをクリックしたら閉じる
menuLinks.forEach(link => {
  link.addEventListener('click', (event) => {
    menu.classList.remove('open');
    enableScroll();
  });
});

動作の仕組み

現在のスクロール位置を保存
メニューが開かれるとき、現在のスクロール位置を保存します。これにより、メニューを閉じた後、スクロール位置が元の位置に戻ります。

scrollPosition = window.scrollY;

position: fixed;を適用してスクロールを完全に停止
メニューが開くと、document.body.style.position = 'fixed';により、ページ全体が固定され、スクロールを防ぎます。

スクロール関連のイベントをpreventDefault()で阻止
touchmovewheelなどのスクロールイベントをpreventDefault()で阻止し、ユーザーがスクロールできないようにします。

メニューを閉じたらスクロール位置を元に戻す
メニューを閉じる際に、window.scrollTo()で保存したスクロール位置にページを戻します。

iOS特有のバグとその回避方法

iOSでは、position: fixedを使った際にスクロールが正しく機能せず、画面がずれる問題が発生することがあります。そこで、今回は以下の方法でiOS特有のバグを回避しています。

  • overflow: hidden;だけでは防げないスクロールを完全に停止
    position: fixed;topの設定で、スクロール禁止を強制的に適用することで、iOSのバグを回避しています。
  • iOSでも動作が安定する
    これにより、iOSでも確実にスクロール禁止が動作し、バグを防げます。

どちらを使うべきか?

この方法を使うことで、iOSでもバグを回避し、Androidでも確実に動作します。スクロール位置を記憶することで、メニューを閉じても元のスクロール位置に戻り、スムーズなユーザー体験が提供できます。

サンプル

See the Pen Side-menu-button by kasasagi_kmnmc (@kasasagi_kmnmc) on CodePen.

まとめ

この記事では、モバイルメニューの作成方法と、スクロール禁止およびアンカーリンクでメニューが閉じる機能の実装方法を解説しました。iOSでもバグを回避したスクロール制御を取り入れることで、どのデバイスでも安定して動作します。

初心者の方でも理解しやすいように、実装方法とそのメリットについても説明しました。ぜひ、この記事を参考にして、自分のWebサイトでスマートフォン向けの快適なメニューを作成してみてください。

タイトルとURLをコピーしました