【jQuery】追従ヘッダーの高さに合わせてアンカーリンクの移動先を調整する方法|オフセット補正完全ガイド

jQuery

固定ヘッダー(追従ヘッダー)があるサイトでアンカーリンクをクリックすると、移動先の見出しがヘッダーの下に隠れてしまうことがあります。本記事ではjQueryでヘッダーの高さを動的に取得してオフセット補正する方法を、基本からレスポンシブ対応・CSSのみの代替手段まで丁寧に解説します。

この記事でわかること

  • 追従ヘッダーが原因でリンク先が隠れる問題の原因と解決策
  • jQueryでヘッダー高さを動的取得してオフセット補正する方法
  • レスポンシブ対応(画面幅でヘッダー高さが変わる場合)
  • CSSのscroll-padding-topによる代替手段
  • URLハッシュ直アクセス時の対応(ページロード時のずれ修正)
スポンサーリンク

1. 問題の原因

固定ヘッダーを position: fixed で実装すると、ヘッダーはドキュメントフローから外れます。アンカーリンクのデフォルト動作は「要素の上端をビューポートの上端に合わせる」ため、固定ヘッダーがある場合はその分だけ見出しが隠れてしまいます。

よくある失敗パターン
固定ヘッダーの高さをCSSで設定しても、アンカーリンクのジャンプ先は調整されません。JavaScriptかCSSの scroll-padding-top で補正が必要です。

2. jQueryでオフセット補正する(基本実装)

ヘッダーの高さを outerHeight() で取得し、スクロール位置から引き算します。

$(function () {
  // アンカーリンクのクリックイベント
  $('a[href^="#"]').on('click', function (e) {
    e.preventDefault();

    var target  = $(this.hash);
    if (!target.length) return;

    // ヘッダーの高さを動的に取得(paddingを含む外側の高さ)
    var headerH = $('header').outerHeight() || 0;
    var margin  = 16;  // ヘッダーと見出しの余白
    var pos     = target.offset().top - headerH - margin;

    $('html, body').animate({ scrollTop: pos }, 500);
  });
});
outerHeight() vs height() vs innerHeight()

  • height(): コンテンツ高さのみ
  • innerHeight(): コンテンツ + padding
  • outerHeight(): コンテンツ + padding + border(通常はこれを使う)
  • outerHeight(true): コンテンツ + padding + border + margin

3. レスポンシブ対応(画面幅でヘッダー高さが変わる場合)

スマホとPCでヘッダー高さが異なる場合、クリック時に動的取得すれば自動的に対応できます。静的な変数に保存すると画面回転・リサイズで古い値が使われるため注意が必要です。

$(function () {
  $('a[href^="#"]').on('click', function (e) {
    e.preventDefault();
    var target  = $(this.hash);
    if (!target.length) return;

    // クリック時に毎回取得(レスポンシブ対応)
    var headerH = $('#site-header').outerHeight() || 0;

    var pos = target.offset().top - headerH - 16;
    $('html, body').animate({ scrollTop: pos }, 500);
  });
});

4. URLハッシュ直アクセス時のずれを修正

ページロード時に URL に #section1 などのハッシュが含まれている場合、ブラウザはデフォルト動作でスクロールしますが固定ヘッダー分のオフセットは補正されません。ロード完了後にスクロール位置を再調整します。

$(function () {
  // ページロード時のハッシュ補正
  if (location.hash) {
    var target  = $(location.hash);
    if (target.length) {
      // 一瞬待ってからスクロール(ブラウザの初期スクロールを上書き)
      setTimeout(function () {
        var headerH = $('header').outerHeight() || 0;
        var pos     = target.offset().top - headerH - 16;
        $('html, body').scrollTop(pos);  // アニメーションなし
      }, 100);
    }
  }

  // 通常のアンカーリンク
  $('a[href^="#"]').on('click', function (e) {
    e.preventDefault();
    var target  = $(this.hash);
    if (!target.length) return;
    var headerH = $('header').outerHeight() || 0;
    $('html, body').animate({ scrollTop: target.offset().top - headerH - 16 }, 500);
  });
});

5. CSSのscroll-padding-topによる代替手段

jQuery不要で解決できるモダンな方法です。scroll-padding-tophtml タグに指定すると、スクロール時のオフセットをCSSだけで設定できます。

html {
  scroll-behavior: smooth;        /* スムーズスクロール */
  scroll-padding-top: 80px;       /* ヘッダー高さ分 */
}

ヘッダー高さをCSS変数で管理するとより柔軟です:

:root {
  --header-h: 70px;  /* ヘッダー高さ */
}
html {
  scroll-padding-top: var(--header-h);
}
header {
  height: var(--header-h);
}
scroll-padding-topの注意点
IE非対応です。また、ヘッダー高さが動的に変わる場合(追従後縮小など)はCSSの固定値では対応しきれないため、その場合はJavaScriptと組み合わせてください。

6. ターゲット要素に疑似要素でオフセットを設定する方法

見出しなどのターゲット要素に ::before 疑似要素で透明な高さを設定し、その分上に余白を作る古典的な手法です。JavaScriptが不要な反面、全セクションにCSSを書く必要があります。

/* ターゲットになる全セクションに適用 */
.section {
  scroll-margin-top: 80px;  /* ヘッダー高さ分 */
}

/* 古い書き方(::before で疑似要素) */
.section::before {
  content: "";
  display: block;
  height: 80px;      /* ヘッダー高さ */
  margin-top: -80px; /* 負のマージンで隠す */
  visibility: hidden;
  pointer-events: none;
}

7. 方法の比較

方法 jQuery不要 IE対応 動的ヘッダー高さ対応 実装コスト
jQuery offset補正 不要
CSS scroll-padding-top 不要 非対応 限定的(CSS変数) 最低
CSS scroll-margin-top 不要 非対応 限定的 低(全要素にCSS)
::before疑似要素 不要 不可 中(全要素にCSS)

まとめ

固定ヘッダーによるアンカーリンクのずれは、jQueryなら outerHeight() で動的にヘッダー高さを取得して補正するのが確実です。モダン環境(IE非対応可)なら scroll-padding-top または scroll-margin-top のCSSのみで解決できます。動的にヘッダー高さが変わるサイトではJavaScriptによる動的取得が最も信頼性が高いです。

関連記事: jQueryでアンカーリンクのスムーズスクロールを実装する方法 / スクロールで追従するヘッダーの実装完全ガイド

よくある質問(FAQ)

Q追従ヘッダーがあるのにアンカーリンクで見出しが隠れます
Aヘッダー高さ分だけスクロール位置をオフセット補正する必要があります。jQueryなら target.offset().top - $("header").outerHeight() - 余白px で計算してください。CSSのみで解決するなら html { scroll-padding-top: ヘッダー高さ; } も有効です。
QouterHeight()とheight()の違いは何ですか?
Aheight() はコンテンツ高さのみ、outerHeight() はpadding+borderを含む高さを返します。ヘッダーの実際の表示高さを取得するには通常 outerHeight() を使います。
QURLに#ハッシュを付けて直接アクセスしたときもずれます
Aページロード時にハッシュがある場合は setTimeout() で少し待ってから$(location.hash).offset().top - headerH にスクロール位置を合わせます。
QスマホとPCでヘッダー高さが違う場合はどうすればいいですか?
Aクリック時に毎回 $('header').outerHeight() で動的取得すれば自動的に対応できます。初期化時に変数に保存すると画面回転・リサイズで古い値が使われるため、毎回取得する方が確実です。
QjQueryなしでオフセット補正するには?
ACSSの scroll-padding-top(IE非対応)か scroll-margin-top を使います。JavaScriptを使う場合は element.getBoundingClientRect().top + window.scrollY - headerHeight で計算して window.scrollTo({ top: pos, behavior: "smooth" }) でスクロールします。