スクロールに応じてヘッダーを固定表示(追従)させる実装は、ユーザビリティを高める定番テクニックです。本記事では指定位置でfixedに切り替える基本実装から、ヘッダー縮小・上スクロール時のみ表示・モバイル対応まで、jQuery・バニラJS両方のコードで解説します。
- スクロール量に応じてヘッダーをfixedに切り替える方法
- 追従時にスムーズなトランジションを付ける方法
- スクロールダウン時に隠れ、アップ時に現れるヘッダーの実装
- 追従時にヘッダーを縮小する実装
- コンテンツのズレ(ガタツキ)を防ぐ方法
1. 基本実装:指定位置で追従開始
最もシンプルな追従ヘッダーです。スクロール量が一定値を超えたら .is-fixed クラスを付与してCSSで固定します。
HTML
<header class="site-header" id="site-header">
<div class="header-inner">
<div class="site-logo">サイト名</div>
<nav class="header-nav">
<ul>
<li><a href="#">メニュー1</a></li>
<li><a href="#">メニュー2</a></li>
<li><a href="#">メニュー3</a></li>
</ul>
</nav>
</div>
</header>
<div id="header-placeholder"></div> <!-- コンテンツのガタツキ防止 -->
CSS
.site-header {
background: #1e293b;
padding: 20px;
transition: box-shadow 0.3s, padding 0.3s;
}
.header-inner {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
}
.site-logo {
color: #fff;
font-size: 22px;
font-weight: bold;
}
.header-nav ul { display: flex; gap: 24px; list-style: none; }
.header-nav a { color: #cbd5e1; text-decoration: none; }
.header-nav a:hover { color: #fff; }
/* 追従時に付くクラス */
.site-header.is-fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
box-shadow: 0 2px 12px rgba(0,0,0,0.25);
animation: slideDown 0.3s ease forwards;
}
@keyframes slideDown {
from { transform: translateY(-100%); }
to { transform: translateY(0); }
}
jQuery
$(function () {
var $header = $('#site-header');
var $placeholder = $('#header-placeholder');
var threshold = 200; // この位置を超えたら追従開始(px)
$(window).on('scroll.fixedHeader', function () {
var scrollTop = $(this).scrollTop();
if (scrollTop > threshold) {
if (!$header.hasClass('is-fixed')) {
// ガタツキ防止: ヘッダーが fixed になる分の高さを確保
$placeholder.height($header.outerHeight());
$header.addClass('is-fixed');
}
} else {
$header.removeClass('is-fixed');
$placeholder.height(0);
}
});
});
position: fixed にするとヘッダーがドキュメントフローから外れ、その分コンテンツが上にずれます。同じ高さの #header-placeholder を使って隙間を補填することでガタツキを防げます。2. 追従時にヘッダーを縮小する
大きなヘッダーが追従すると読みにくくなります。追従時にパディングとフォントサイズを縮小することで、コンテンツの視認性を確保します。
.site-header {
padding: 20px;
transition: padding 0.3s, font-size 0.3s, box-shadow 0.3s;
}
.site-header.is-fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
padding: 10px 20px; /* 縮小 */
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.site-header.is-fixed .site-logo {
font-size: 16px; /* ロゴも縮小 */
}
3. 下スクロールで隠れ・上スクロールで現れるヘッダー
読み物コンテンツで使われるテクニックです。スクロールダウン中はヘッダーを非表示にし、アップ時のみ表示することでコンテンツを広く使えます。
$(function () {
var $header = $('#site-header');
var lastScroll = 0;
var threshold = 80; // ヘッダーの高さ程度
$(window).on('scroll.smartHeader', function () {
var currentScroll = $(this).scrollTop();
if (currentScroll <= 0) {
// 最上部: 常に表示
$header.removeClass('is-hidden').addClass('is-fixed');
return;
}
if (currentScroll > lastScroll && currentScroll > threshold) {
// 下スクロール: ヘッダーを上に隠す
$header.addClass('is-hidden');
} else if (currentScroll < lastScroll) {
// 上スクロール: ヘッダーを表示
$header.removeClass('is-hidden');
}
lastScroll = currentScroll;
});
});
.site-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
transition: transform 0.35s ease;
}
.site-header.is-hidden {
transform: translateY(-100%); /* 上に隠す */
}
4. 閾値をヘッダー自身の高さに合わせる
スクロール開始の閾値をハードコードするとレスポンシブで崩れることがあります。ページロード時にヘッダー高さを動的に取得するのが確実です。
$(function () {
var $header = $('#site-header');
var threshold = $header.outerHeight(); // ヘッダー高さを自動取得
$(window).on('scroll', function () {
$header.toggleClass('is-fixed', $(this).scrollTop() > threshold);
});
});
5. バニラJSでの実装(IntersectionObserver)
jQuery不要のモダンな実装例です。IntersectionObserver を使うことで scroll イベントリスナーより効率的に実装できます。
const header = document.getElementById('site-header');
const sentinel = document.getElementById('scroll-sentinel'); // ヘッダー直後に置く要素
const observer = new IntersectionObserver(([entry]) => {
header.classList.toggle('is-fixed', !entry.isIntersecting);
}, { threshold: 0 });
observer.observe(sentinel);
jQuery の scroll イベントは高頻度で発火します。パフォーマンスが気になる場合は
requestAnimationFrame や lodash の _.throttle で間引くか、IntersectionObserver に切り替えましょう。6. jQueryとCSS position:stickyの比較
| 比較項目 | jQuery fixed切替 | CSS position:sticky |
|---|---|---|
| 実装の複雑さ | 中(JSが必要) | 低(CSSのみ) |
| 縮小・アニメ | 自由にカスタム可 | 限定的 |
| ガタツキ対策 | placeholderが必要 | 不要 |
| IE対応 | jQuery版は対応 | IE非対応 |
| パフォーマンス | scrollイベント負荷あり | ブラウザ最適化済み |
ヘッダーが常に追従するだけなら
position: sticky; top: 0; をCSSに書くだけです。JavaScriptは一切不要でパフォーマンスも優れています。7. よくあるトラブルと解決策
固定後にコンテンツが上にガタつく
ヘッダーと同じ高さの placeholder 要素をヘッダー直後に配置し、is-fixed 付与時にその高さをJSでセットしてください(セクション1のサンプル参照)。
追従ヘッダーがコンテンツの上に重なる
z-index の設定が必要です。モーダルやドロップダウンが表示されている場合はそちらの z-index をヘッダーより大きくしてください。
iOSでスクロール中に追従が遅れる
iOS Safari では慣性スクロール中にイベントが発火しないことがあります。CSS の position: sticky で代替できる場合はそちらを検討してください。
まとめ
追従ヘッダーの基本はスクロール量の監視と is-fixed クラスの付け外しです。ガタツキ防止の placeholder、縮小効果のトランジション、上スクロール時のみ表示するスマートヘッダーなど、要件に合わせて組み合わせてください。
関連記事: 追従ヘッダーの高さに合わせてアンカーリンクの移動先を調整する方法 / スムーズスクロール機能付きのトップに戻るボタンを作る
よくある質問(FAQ)
position: sticky; top: 0; を使うとJavaScript不要で追従ヘッダーが作れます。縮小やアニメーションが必要な場合は IntersectionObserver をバニラJSで使う方法があります。placeholder 要素を置き、position: fixed 切り替え時にJSでその高さをセットします。これでヘッダーがフローから外れた分の空白を補填できます。transform: translateY(-100%)、上向きなら元に戻すCSSクラスを付け外しします。$header.outerHeight() で動的に取得すると、レスポンシブでヘッダー高さが変わっても正確に動作します。transition: transform 0.3s ease; を指定するか、@keyframes slideDown でスライドインアニメーションを付けます。jQueryの animate() よりCSSトランジションの方がパフォーマンスが優れています。

