- JavaScript
fullPage.jsの使い方|ライセンス・動きを実測しscroll-snapと比較
結論
fullPage.jsは、ページを画面1枚ぶんの「セクション」に分け、ホイール・キー・スワイプ1回で次の画面へ移すJavaScriptライブラリです。最新の4.0.41はGPLv3か商用ライセンスで配布されていて、ソースを公開しないサイトで使うには商用ライセンスと licenseKey が要ります。
この記事でわかること
- ライセンスの選び方と、licenseKey が無いときに出た表示
- 最小構成のコードと、コールバック・キー・戻る・スワイプの実測
- CSS scroll-snapだけで作る代替との比較と判断表
fullPage.jsを使うかどうかは、機能より先に「ライセンスの条件を満たせるか」で決まります。満たせない、またはスライドやコールバックが要らないなら、CSSの scroll-snap でも似たページを作れます。
flowchart TD
A[1画面ずつ進むページを作りたい] --> B{GPLv3で公開する<br>または商用ライセンスを買う?}
B -->|いいえ| C[CSS scroll-snap<br>で作る]
B -->|はい| D{スライド・コールバック<br>戻るボタン連動が要る?}
D -->|はい| E[fullPage.js 4]
D -->|いいえ| C
よく調べられることの答えを先にまとめます。
| やりたいこと | いちばん早い方法 | 詳しい節 |
|---|---|---|
| fullPage.jsを無料で使えるか知りたい | GPLv3で公開するプロジェクトなら無料。それ以外は商用ライセンス | fullPage.jsのライセンス |
| fullpage.jsの使い方を最短で | CSS・JSを読み込み、new fullpage('#fullpage', {...}) |
fullPage.jsの使い方(最小構成) |
| 横にスライドさせたい | セクションの中に class="slide" を並べる |
スライド(横スクロール) |
| メニューと現在地を連動させたい | anchors と menu、data-menuanchor |
メニュー連動とアンカー |
| 移動したときに処理を動かしたい | afterLoad(着いた後)・onLeave(離れる前) |
コールバックの順番 |
| スマホでは普通にスクロールさせたい | responsiveWidth: 768 など |
スマホ対応 |
| 長いセクションが切れる | scrollOverflow(既定で true)と scrollBar: false |
長いセクション(scrollOverflow) |
| 動かない・エラーが出る | コンソールの文言で原因表を引く | よくあるエラーとつまずき |
| ライブラリ無しで作りたい | scroll-snap-type: y mandatory |
CSS scroll-snapで作る代替 |
確認環境:Ubuntu 24.04.5 LTS の上で、npm 10.9.9(Node.js 22.23.4)で作業場所に fullPage.js 4.0.41 を入れ、Python 3.13.18 の http.server で 127.0.0.1 に置いたページを Chromium 153.0.8010.12(Playwright 1.63.0 から操作)で開きました。デスクトップは 1280×800、スマホは 375×667(タッチ有効)の画面で、ホイール・キー・Tab・タッチのスワイプ・戻る/進むを送り、コールバックの記録、URL、history.length、表示位置を数値で取っています。外部への通信は Playwright で 127.0.0.1 以外をすべて止めた状態で確かめ、fullPage.js が外へ出したリクエストは0件でした。jQuery 形式の初期化は jQuery 3.7.1 で確かめています。ライセンスと料金は公式の料金ページと GitHub の README、版の履歴は npm の公開情報、CSS scroll-snap と prefers-reduced-motion は MDN で確かめました。iPhone・Android の実機の挙動は確かめていません。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「fullPage.jsの使い方|ライセンス・動きを実測しscroll-snapと比較」
fullPage.jsとは(1画面ずつスクロールするライブラリ)
この節では、fullPage.jsで何ができ、どんなページに使われるのかが分かります。
fullPage.jsは、Alvaro Trigo 氏が作っている、全画面のセクションを1枚ずつ切り替えるライブラリです。ホイールを1回回す、矢印キーを1回押す、スマホで1回スワイプする、のどれでも「次の画面」にぴったり止まります。製品紹介・ポートフォリオ・特設ページでよく使われます。
しくみは単純で、セクションを縦に並べた入れ物(ラッパー)を CSS の transform で画面1枚ぶんずつ動かしています。手元で初期化した直後は、html に fp-enabled、body に fp-viewing-top のような今いる場所を表すクラスが付き、body と html の overflow は hidden になりました。
版の移り変わり
検索で見つかる記事は書かれた時期によって版が違い、書き方とライセンスが変わっています。npm に公開されている情報では次のとおりです。
| 版 | 公開日(npm) | npm のライセンス欄 | 書き方 |
|---|---|---|---|
| 2.9.7 | 2018年4月9日 | MIT | jQuery プラグイン($('#fullpage').fullpage()) |
| 3.0.0 | 2018年6月26日 | GPL-3.0 | jQuery 無しの new fullpage() |
| 4.0.0 | 2022年4月10日 | GPL-3.0 | 3系と同じ形。licenseKey が必須に |
| 4.0.41(最新) | 2026年3月4日 | GPL-3.0 | 同上。jQuery があれば $.fn.fullpage も使える |
2系は MIT ですが、2018年から更新されていません。この記事のコードはすべて4.0.41で動かしています。
fullPage.jsのライセンス(GPLv3と商用・licenseKey)
この節では、fullPage.jsを無料で使える条件と、商用ライセンスの種類、licenseKey を入れないと何が起きるかが分かります。
3系以降の fullPage.js は「GPLv3 で公開するオープンソース」なら無料、それ以外のサイト・テーマ・アプリでは商用ライセンスが要ります。公式の料金ページは、GPLv3 で使う条件を「プロジェクトを公開し、クレジットを表示し、GPLv3 でライセンスすること」と書いています。会社のコーポレートサイトや受託で作る LP のように、ソースを GPLv3 で公開しないものは商用ライセンスの対象です。
GPLv3 で使う(無料)
自分のプロジェクトも GPLv3 で公開する
fullPage.js を使っていることを目立つ形で示す
README によれば、オープンソース用のキーを申請できる
商用ライセンスで使う(有料)
ソースを公開しないサイト・テーマ・アプリ
購入すると licenseKey が発行される
拡張機能(extensions)は別売り
商用ライセンスの種類と料金(2026年10月29日時点)
公式の料金ページ(年額表示)で確かめた内容です。料金は変わるため、買う前に公式で確かめます。
| プラン | 料金(年額) | 開発者 | 主な内容 |
|---|---|---|---|
| Hobby | 15米ドル | 1人 | 1プロダクト。基本のサポート(GitHub) |
| Professional | 39米ドル | 8人まで | 3プロダクト(料金カードの表記)。基本のサポート |
| Business | 159米ドル | 無制限 | プロダクト無制限。3か月のプレミアムサポート |
| OEM/SaaS | 個別見積もり | 個別 | 顧客ごとに条件を決める |
料金ページには年額のほかに「一度払い(One-time Payment)」の切り替えもあり、FAQ では、一度払いはメジャーバージョンまで更新を受けられると説明しています。なお、同じ料金ページの中で、Professional の対象サイト数は料金カードと比較表・FAQ で書き方が分かれていました。複数のサイトで使う予定なら、購入前に販売元へ確かめると確実です。
licenseKey を入れないとき・違うキーのときに出た表示
手元の確認用ページで、licenseKey を書かずに初期化すると、Chromium のコンソールに次のエラーが出ました。
fullPage: Fullpage.js requires a `licenseKey` option. Read about it on the following website:
fullPage: https://alvarotrigo.com/fullPage/docs/#licensekey
正しくない文字列を入れたときは、文言が変わりました。
fullPage: Incorrect `licenseKey`. Get one for fullPage.js version 4 here:
fullPage: https://alvarotrigo.com/fullPage/pricing
どちらの場合も、ページの中に「Made with fullPage.js」というリンク(class="fp-watermark")が追加され、最後のセクションの右下に表示されました。スクロールの動き自体は止まりませんでしたが、動くことと、ライセンス上使ってよいことは別です。公開するページでは、条件に合うライセンスのキーを licenseKey に入れます。
ライセンスの判断で避けたいこと
決め手:公開するソースを GPLv3 にできるか、費用をかけられるか
fullPage.jsの使い方(最小構成のコード)
この節では、fullPage.jsを読み込み、4つのセクションとメニューを持つページを動かすまでのコードが分かります。
必要なのは、CSS と JS の2ファイル、セクションを入れるラッパー、初期化の1行です。4系は jQuery を使いません。
ファイルを用意する
npm なら npm install fullpage.js。CDN なら jsDelivr・unpkg・cdnjs のどれかで、版を固定した URL を使う。
DOCTYPE とラッパーを書く
先頭に <!DOCTYPE html>。セクションは class="section" を付けて、id を持つラッパーの中に並べる。ラッパーを body にはできない。
body の最後で初期化する
ラッパーより後ろで new fullpage('#fullpage', {...}) を呼ぶ。head の中で呼ぶと、要素がまだ無くてエラーになる。
手元で動かしたページです。セクションのクラス名が合っていれば、要素は main・section・article でも動きました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>fullPage.js のサンプル</title>
<link rel="stylesheet" href="fullpage.min.css">
<style>
#menu { position: fixed; top: 0; left: 0; z-index: 70; display: flex; gap: 8px; margin: 0; padding: 8px; list-style: none; background: #fff; }
#menu li.active a { font-weight: bold; }
</style>
</head>
<body>
<ul id="menu">
<li data-menuanchor="top"><a href="#top">Top</a></li>
<li data-menuanchor="service"><a href="#service">Service</a></li>
<li data-menuanchor="works"><a href="#works">Works</a></li>
<li data-menuanchor="contact"><a href="#contact">Contact</a></li>
</ul>
<main id="fullpage">
<section class="section"><h2>Top</h2></section>
<section class="section"><h2>Service</h2></section>
<section class="section">
<article class="slide"><h2>Work 1</h2></article>
<article class="slide"><h2>Work 2</h2></article>
<article class="slide"><h2>Work 3</h2></article>
</section>
<section class="section"><h2>Contact</h2></section>
</main>
<script src="fullpage.min.js"></script>
<script>
new fullpage('#fullpage', {
licenseKey: 'YOUR_KEY_HERE',
anchors: ['top', 'service', 'works', 'contact'],
menu: '#menu',
navigation: true,
slidesNavigation: true,
sectionsColor: ['#f2f2f2', '#dfe9f3', '#fbe9d0', '#e8f5e9'],
afterLoad: function (origin, destination, direction, trigger) {
console.log('afterLoad', destination.anchor, direction, trigger);
}
});
</script>
</body>
</html>
このページでホイールを1回、下矢印を1回、右矢印を1回送ったときのコンソールです(licenseKey は仮の文字列なので、先頭にライセンスのエラーが出ています)。
error: fullPage: Incorrect `licenseKey`. Get one for fullPage.js version 4 here:
error: fullPage: https://alvarotrigo.com/fullPage/pricing
log: afterLoad top null null
log: afterLoad service down wheel
log: afterLoad works down keydown
(このあと URL は http://127.0.0.1:8163/min.html#works/1 になった)
読み込んだ直後にも afterLoad が1回呼ばれ、そのときの direction と trigger は null でした。2回目からは、何で動いたか(wheel・keydown など)が trigger に入ります。
CDN と npm での読み込み
CDN を使うときは、版を固定した URL にします。版を書かない URL は、新しい版が出たときに知らないうちに中身が変わります。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullpage.js@4.0.41/dist/fullpage.min.css">
<script src="https://cdn.jsdelivr.net/npm/fullpage.js@4.0.41/dist/fullpage.min.js"></script>
npm で入れてビルドツールから使う場合は、次の形です。dist には fullpage.js(200KB)・fullpage.min.js(60KB)・fullpage.extensions.min.js(68KB、拡張機能つき)と CSS が入っていました。React・Vue・Angular 用の公式ラッパーは README から案内されています。
import fullpage from 'fullpage.js';
import 'fullpage.js/dist/fullpage.min.css';
const api = new fullpage('#fullpage', { licenseKey: 'YOUR_KEY_HERE' });
jQuery の書き方(以前の記事のコード)を使う場合
2系の記事にある $('#fullpage').fullpage({...}) は、jQuery を読み込まずに書くと $ is not defined で止まりました。4.0.41 の本体には、読み込み時に window.jQuery があれば $.fn.fullpage を用意する処理が入っていて、jQuery 3.7.1 を先に読み込むと、この書き方でも初期化でき、$.fn.fullpage.moveSectionDown() も動きました。新しく書くなら new fullpage() の形に寄せるほうが、jQuery を外せて身軽です。jQuery そのものと、今の JavaScript への置き換えは、関連記事「jQueryとは|使い方・Ajax・fetchとの対応表を4.0の実行結果で解説」で扱っています。
スライド(横スクロール)の作り方
この節では、1つのセクションの中を左右に切り替える「スライド」の書き方と、キー操作での動きが分かります。
セクションの中に class="slide" の要素を2つ以上並べると、そのセクションだけ横に切り替わるようになります。左右の矢印ボタン(controlArrows、既定で true)が自動で付き、slidesNavigation: true で下に点のナビゲーションも出ます。
Works のセクションで右矢印キーと左矢印キーを送ったときの記録です(コールバックの引数は、セクション番号・元のスライド・行き先のスライド・方向・きっかけ。番号は0から)。
ArrowRight → #works/1 onSlideLeave 2 0→1 right keydown / afterSlideLoad(701ms後)
ArrowRight → #works/2 onSlideLeave 2 1→2 right keydown / afterSlideLoad(701ms後)
ArrowRight → #works onSlideLeave 2 2→0 right keydown / afterSlideLoad(701ms後)
ArrowLeft → #works/2 onSlideLeave 2 0→2 left keydown / afterSlideLoad(704ms後)
- 最後のスライドで右を押すと、最初に戻りました。
loopHorizontalが既定で true のためです。端で止めたいときはloopHorizontal: falseにします。 - URL のスライド番号は0から数え、最初のスライドは番号が付きません。 2枚目が
#works/1、3枚目が#works/2です。#works/2を直接開くと、3枚目が表示されました。 - スライドの無いセクション(Top)で右矢印を押しても、何も起きず、コールバックも呼ばれませんでした。
- スマホ幅(375×667)でタッチの横スワイプを送ると、
#worksから#works/1に進みました。
写真のカルーセルのように、自動再生・何枚かを並べて見せる・サムネイル連動といった機能が欲しい場合は、全画面のスライドより専用のライブラリが向きます。関連記事「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」で扱っています。
メニュー連動とアンカー(URLと戻るボタン)
この節では、固定メニューと今いるセクションを連動させる方法と、URL のハッシュ・戻るボタンがどう動くかが分かります。
anchors にセクションごとの名前、menu にメニューのセレクター、メニューの各項目に同じ名前の data-menuanchor を書くと、表示中の項目に active クラスが付きます。URL も #service のように変わるので、特定のセクションを直接開くリンクを作れます。
anchors
セクションの順に名前を並べる。URL のハッシュになる。
ページ内の id と重ねない
menu
メニューのセレクター。中の li に active が付く。
メニューはラッパーの外に置く
navigation
右端に点のナビゲーションを出す。位置は navigationPosition。
navigationTooltips で文字も
recordHistory
移動のたびに履歴を積み、戻るボタンで1つ前へ戻す。
既定で true
戻るボタンの実測
トップから順に移動し、スライドも動かしたあとに、Playwright で戻る・進むを送りました。
| 操作 | 結果 |
|---|---|
15回ほど移動したあとの history.length |
16(移動とスライドの切り替えのたびに1つ増えた) |
| 戻る(1回目) | #works/2 → #works(直前の位置へ) |
| 戻る(2回目・3回目) | #works/2、#works/1 とスライドも1つずつ戻った |
| 進む | #works/2 へ |
recordHistory: false で2回移動 |
ハッシュは #c まで変わったが、履歴は増えなかった |
autoScrolling: false で2回移動 |
ハッシュは変わったが、履歴は増えなかった(README のとおり recordHistory が切れる) |
lockAnchors: true |
読み込み後にハッシュを #c に変えても移動しなかった |
既定のままだと、何画面か見てから戻るボタンを押しても、前のページではなく1つ前のセクションに戻ります。トップから来た人が「戻る」で元の検索結果に帰りたいページなら、recordHistory: false にして、ハッシュの変化だけを残すほうが使いやすくなります。
固定メニューは、ラッパー(#fullpage)の外に置きます。ラッパーの中のセクションに position: fixed の要素を置いたところ、1画面動いたあとの位置が top: -800px になり、画面から消えました。ラッパーの外に出すと 0 のままでした。ラッパーに transform が掛かると、その中の fixed は画面ではなくラッパーを基準にするためです。
コールバックの順番と使い方(onLeave・afterLoad)
この節では、どのコールバックがどの順番で呼ばれ、移動を止めたり、着いたあとに処理を動かしたりする方法が分かります。
セクションの移動では beforeLeave → onLeave → (移動のアニメーション)→ afterLoad の順に呼ばれました。ホイール1回の記録は次のとおりで、afterLoad は onLeave の702ミリ秒後でした。
[882, 'beforeLeave', 0, 1, 'down', 'wheel']
[882, 'onLeave', 0, 1, 'down', 'wheel']
[1584, 'afterLoad', 0, 1, 'down', 'wheel']
(先頭の数字はページを開いてからのミリ秒。続いて、元の番号・行き先の番号・方向・きっかけ)
| コールバック | 呼ばれるとき | 引数 | 主な使い道 |
|---|---|---|---|
beforeLeave |
離れる直前(いちばん先) | origin, destination, direction, trigger | false を返して止める |
onLeave |
離れ始めるとき | 同上 | アニメーションの開始・false で止める |
afterLoad |
着いたあと(読み込み直後にも1回) | 同上 | 中身の表示・アクセス解析の送信 |
onSlideLeave / afterSlideLoad |
スライドを離れる・着いた | section, origin, destination, direction, trigger | スライドごとの処理 |
afterRender |
組み立てが終わったとき | なし | 他のライブラリの初期化 |
afterResize |
画面の大きさが変わったあと | width, height | 大きさに合わせた調整 |
afterResponsive |
レスポンシブの切り替え時 | isResponsive | スマホ用の見た目へ |
onScrollOverflow |
長いセクションの中をスクロールしたとき | section, slide, position, direction | 読み進めた位置の表示 |
移動を止める(false を返す)
onLeave で2番目のセクションへの移動のときだけ false を返すようにして、ホイールを1回送りました。記録は onLeave の1行だけで、afterLoad は呼ばれず、Top のままでした。入力途中のフォームから離れさせたくない、といった場面で使えます。
new fullpage('#fullpage', {
licenseKey: 'YOUR_KEY_HERE',
onLeave: function (origin, destination, direction, trigger) {
// フォームに入力中なら下へ進ませない
if (origin.anchor === 'contact' && direction === 'down' && isEditing()) {
return false;
}
}
});
着いたセクションでアクセス解析を送る
fullPage.jsの移動はページの読み込みではないため、アクセス解析のページビューは自動では増えません。afterLoad で送るのが定番です。次の例は、gtag.js が読み込まれているときだけ送る形です。手元では gtag を「受け取った引数を記録するだけの関数」に置き換えて動かし、読み込み直後に1回(service)、ホイールで移動したあとに1回(works)、page_view が記録されました。送信先には接続していません。
afterLoad: function (origin, destination) {
if (typeof gtag === 'function') {
gtag('event', 'page_view', {
page_location: location.href,
page_title: document.title + ' - ' + destination.anchor
});
}
}
引数の origin・destination は、index(0からの番号)・anchor(anchors の名前)・item(要素)・isFirst・isLast を持つオブジェクトでした。一方、メソッドの fullpage_api.getActiveSection() が返すオブジェクトでは index が関数で、getActiveSection().index() と呼ぶと番号が取れました。コールバックの引数と同じつもりで .index と書くと、数字ではなく関数が返ります。
オプションとメソッドの早見表
この節では、よく使うオプションの既定値と、外から操作するメソッドが一覧で分かります。
オプションは README に70個近くありますが、最初に触るのは次の十数個です。既定値は README で確かめ、動きは手元で確かめています。
| オプション | 既定値 | 働き |
|---|---|---|
licenseKey |
null | ライセンスのキー。README では必須 |
anchors / menu |
[] / false | URL のハッシュとメニューの連動 |
navigation / navigationPosition |
false / なし | 点のナビゲーションと位置(left・right) |
scrollingSpeed |
700 | 移動のミリ秒。0・300・1200で実測した値は 1・301・1200ms |
easing / easingcss3 |
easeInOutCubic / ease | 動きの緩急 |
loopTop / loopBottom |
false / false | 端から反対の端へつなぐ |
loopHorizontal |
true | スライドを最後から最初へつなぐ |
scrollOverflow |
true | 長いセクションの中をスクロールできるようにする |
responsiveWidth / responsiveHeight |
0 / 0 | この幅・高さより小さいと、ふつうのスクロールにする |
keyboardScrolling |
true | 矢印キーなどで移動する |
recordHistory / lockAnchors |
true / false | 戻るボタンの履歴・ハッシュでの移動を止める |
verticalCentered |
true | 中身を上下中央に置く |
touchSensitivity |
5 | スワイプで切り替わるまでの距離(画面の%) |
初期化すると、グローバルに fullpage_api ができました(手元では28個のメソッド・プロパティ)。よく使うメソッドです。
| メソッド | 働き |
|---|---|
moveTo('works', 1) |
アンカー名か1からの番号で移動。2つ目はスライド |
silentMoveTo('top') |
アニメーション無しで移動 |
moveSectionDown() / moveSectionUp() |
1つ下・上へ |
moveSlideRight() / moveSlideLeft() |
スライドを左右へ |
setScrollingSpeed(0) |
移動の速さを後から変える |
setAllowScrolling(false) |
ホイール・スワイプでの移動を止める(キーは別) |
setKeyboardScrolling(false) |
キーでの移動を止める |
reBuild() |
中身が増えたあとに高さを計算し直す |
destroy('all') |
元のページに戻す |
destroy('all') を呼ぶと、fp-enabled などのクラス、点のナビゲーション、「Made with fullPage.js」のリンクが消え、body の overflow は visible に戻りました。以前の記事にある $.fn.fullpage.addSection() は、4.0.41 の README のメソッド一覧にはありません。セクションを足したときは、要素を足してから reBuild() を呼ぶか、作り直します。
拡張機能(fadingEffect・parallax など)は別売りで、fullpage.extensions.min.js と拡張機能ごとのキーが要ります(README)。
キーボード操作とTabの動きを確かめた結果
この節では、キーボードだけで操作したときにどこへ移動するか、Tab キーでフォーカスがどう動くかが分かります。
矢印キー・PageUp/PageDown・スペース・Home/End で、それぞれ1画面ずつ(Home/End は最初・最後へ)移動しました。移動のたびに3つのコールバック(beforeLeave・onLeave・afterLoad)が呼ばれています。
| キー | 移動前 | 移動後 |
|---|---|---|
| ↓(ArrowDown) | Top | Service |
| ↑(ArrowUp) | Service | Top |
| PageDown | Top | Service |
| スペース | Service | Works |
| End | Works | Contact(最後) |
| Home | Contact | Top(最初) |
| →(スライドの無いセクション) | Top | 動かない |
Service のセクションに入力欄を置き、フォーカスを当てて「abc」・スペース・下矢印・PageDown を送ると、入力欄の値が abc になるだけで、セクションは移動しませんでした。
Tab キーの動き
ページを開いて Tab を押していったときのフォーカスの順番は次のとおりでした。
Tab 1回目: Top セクションの中のリンク (URL は変わらない)
Tab 2回目: Contact セクションの入力欄 → 画面が Contact へ移動
Tab 3回目: Made with fullPage.js のリンク
Tab 4〜7回目: 右端の点のナビゲーション(top・service・works・contact)
Tab 8回目: body
Tab 9回目: 固定メニューの Top のリンク
- 最初の Tab は、DOM で先にある固定メニューではなく、表示中のセクションの中のリンクに移りました。 fullPage.js が Tab を受け取ってフォーカス先を選んでいるためです。
- Tab 2回目は、Works のスライドのナビゲーションを飛ばして、Contact の入力欄へ移り、画面も Contact へ移動しました。
- 点のナビゲーションのリンクには、画面には見えない
fp-sr-onlyのテキスト(anchors の名前)が入っていました。スクリーンリーダー向けの名前を付けたいときは、anchors を読み上げても意味の通る名前にします。
メニューに届くまで9回の Tab が要るのは、キーボードで使う人には負担です。スキップリンクを置くなどして、公開前に Tab だけで一周して確かめます。
スマホ対応(スワイプとresponsiveWidth)
この節では、スマホ幅でのスワイプの動きと、小さい画面でだけふつうのスクロールに切り替える方法が分かります。
375×667 のタッチ画面で、指で上に300px動かすスワイプを送ると、1回ごとに1セクションずつ進みました。各セクションの高さは画面と同じ667pxでした。
| 設定 | スワイプ1回目 | 2回目 | 3回目 |
|---|---|---|---|
| 既定 | Service へ | Works へ | Contact へ |
responsiveWidth: 768 |
722px スクロール | 1,473px まで | 2,307px まで |
responsiveWidth: 768 を指定すると、幅768px未満では body に fp-responsive と fp-scrollable が付き、読み込み時に afterResponsive(true) が呼ばれました。セクションの高さは、画面に収まるものは667px、長い Contact は中身どおりの2,430px になり、スワイプの勢いのぶんだけ流れるふつうのスクロールになりました。文字の多いページをスマホで読ませるなら、小さい画面ではふつうのスクロールに戻すほうが読みやすくなります。
new fullpage('#fullpage', {
licenseKey: 'YOUR_KEY_HERE',
responsiveWidth: 768, // 768px 未満はふつうのスクロール
afterResponsive: function (isResponsive) {
document.documentElement.classList.toggle('is-sp', isResponsive);
}
});
実機ではアドレスバーの出し入れで画面の高さが変わります。手元では Chromium のスマホ表示までなので、公開前に iPhone・Android の実機で、最後のセクションが切れないかを確かめます。
長いセクション(scrollOverflow)の動き
この節では、画面より長い中身を入れたセクションで、ホイールやキーを使ったときにどう動くかが分かります。
4系では scrollOverflow が既定で true で、画面より長いセクションは、まず中身がスクロールし、端まで来てから次のセクションへ移ります。2,430px の中身を 800px の画面に入れ、ホイールを400ずつ送りました。
| 操作 | 中身のスクロール位置 | セクション |
|---|---|---|
| ホイール下 1〜4回 | 400 → 800 → 1,200 → 1,600 | Contact のまま |
| ホイール下 5〜7回 | 1,630(いちばん下)で止まる | 最後なので移動なし |
| ホイール上 3回 | 1,230 → 830 → 430 | Contact のまま |
| ↓キー 1回目・2回目 | 0 → 40 | Contact のまま |
| PageDown | 740 | Contact のまま |
| End | 1,630(いちばん下) | Contact のまま |
| Home | - | Top へ移動 |
中身がスクロールするたびに onScrollOverflow が呼ばれ、位置(例:830)と方向(up)が渡されました。セクションの中には fp-overflow というスクロール用の要素が作られ、その scrollHeight は2,430、clientHeight は800でした。
scrollBar: true と scrollOverflow を一緒に使うと、コンソールに次の警告が出ました。README も scrollOverflow には scrollBar: false が要ると書いています。
fullPage: Options scrollBar:true and autoScrolling:false are mutually exclusive with scrollOverflow:true. Sections with scrollOverflow might not work well in Firefox
特定のセクションだけスクロールさせたくないときは、そのセクションに fp-noscroll クラスを付けます(README)。
prefers-reduced-motionと読み込みの重さ
この節では、動きを減らす設定の人への配慮と、ライブラリを入れたときの読み込みの重さが分かります。
OS で「視差効果を減らす」「アニメーションを減らす」を選んでいる人(prefers-reduced-motion: reduce)でも、fullPage.js は既定のまま700ミリ秒かけて動きました。Chromium で reduce を有効にし、matchMedia が true を返すことを確かめた上で1画面動かすと、onLeave から afterLoad まで703ミリ秒でした。配布ファイルの中にも prefers-reduced-motion の文字列はありませんでした。
動きを減らす設定の人には、移動の速さを0にします。setScrollingSpeed(0) にしたときの移動は1ミリ秒でした。
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
const api = new fullpage('#fullpage', {
licenseKey: 'YOUR_KEY_HERE',
scrollingSpeed: reduce.matches ? 0 : 700
});
// 設定を途中で変えた人にも合わせる
reduce.addEventListener('change', function (e) {
api.setScrollingSpeed(e.matches ? 0 : 700);
});
読み込みの重さ
127.0.0.1 から同じ4セクションのページを5回ずつ読み込み、DOMContentLoaded までの時間を比べました。ネットワークの遅れが無い環境での目安です。
| 作り方 | 追加のファイル | DOMContentLoaded(5回) |
|---|---|---|
| fullPage.js 4.0.41 | JS 58,805 バイト+CSS 5,519 バイト | 53〜82ms |
| CSS scroll-snap(次の節) | なし(CSS 674 バイト・JS 351 バイトをページ内に) | 23〜32ms |
CSS scroll-snapだけで作る代替と判断表
この節では、ライブラリを使わずに1画面ずつ止まるページを作るコードと、fullPage.jsとの違いを実測で比べた結果が分かります。
CSS の scroll-snap-type: y mandatory を html に、scroll-snap-align: start を各セクションに書くと、スクロールが止まるたびに画面がセクションの先頭へ吸い付きます。メニューの現在地は IntersectionObserver で付けます。
<style>
html { scroll-snap-type: y mandatory; scroll-behavior: smooth; }
body { margin: 0; }
section { height: 100vh; height: 100svh; scroll-snap-align: start; scroll-snap-stop: always;
display: grid; place-items: center; }
.tall { height: auto; min-height: 100vh; }
#menu { position: fixed; top: 0; left: 0; display: flex; gap: 8px; margin: 0; padding: 8px; list-style: none; background: #fff; }
#menu a.active { font-weight: bold; }
.h { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; width: 100vw; height: 100%;
margin: 0; padding: 0; list-style: none; }
.h > li { flex: 0 0 100%; scroll-snap-align: start; display: grid; place-items: center; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
</style>
<ul id="menu">
<li><a href="#top">Top</a></li><li><a href="#service">Service</a></li>
<li><a href="#works">Works</a></li><li><a href="#contact">Contact</a></li>
</ul>
<section id="top"><h2>Top</h2></section>
<section id="service"><h2>Service</h2></section>
<section id="works">
<ul class="h" tabindex="0"><li>Work 1</li><li>Work 2</li><li>Work 3</li></ul>
</section>
<section id="contact" class="tall"><article style="display:block;height:2400px"><h2>Contact</h2></article></section>
<script>
const links = [...document.querySelectorAll('#menu a')];
const io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
links.forEach(function (a) { a.classList.toggle('active', a.hash === '#' + e.target.id); });
history.replaceState(null, '', '#' + e.target.id);
});
}, { rootMargin: '-50% 0px -50% 0px' });
document.querySelectorAll('section').forEach(function (s) { io.observe(s); });
</script>
IntersectionObserver の threshold: 0.6 で書いたところ、画面より長い Contact は表示の割合が6割に届かず、メニューの現在地が Works のまま残りました。rootMargin: '-50% 0px -50% 0px'(画面の中央の線にかかったら現在地)に変えると切り替わりました。IntersectionObserver の詳しい使い方は、関連記事「IntersectionObserver使い方|無限スクロール・遅延読込レシピ集」で扱っています。
同じ操作をして比べた結果
| 確かめたこと | fullPage.js | CSS scroll-snap |
|---|---|---|
| ホイール1回(合成の deltaY 100) | 次のセクションへ | 元の位置に戻った(400以下は戻り、600で次へ) |
| 0.3秒に10回のホイール | 1セクションだけ進んだ | - |
| ↓・PageDown・スペース | 1セクションずつ | 1セクションずつ(800px ずつ) |
| スマホの縦スワイプ | 1回で1セクション | 1回で1セクション(scroll-snap-stop: always) |
| 横のスライド | 矢印キー・スワイプ・ボタン | フォーカスを当てて →(1,280px ずつ) |
| 長いセクション | 中だけスクロールし、端で移動 | ふつうにスクロール |
| 戻るボタン | 移動ごとに履歴が増える | メニューのクリックだけ増える |
| 最初の Tab | 表示中のセクションの中へ | 固定メニューの Top へ |
| 動きを減らす設定 | 自分で対応が要る | CSS のメディアクエリ1行 |
| 移動の時間 | 700ms(変更可) | scrollend まで490ms(ブラウザ任せ) |
| ライセンス | GPLv3 か商用 | 不要 |
ホイールの結果は、Playwright が送る合成のホイールイベントでの値です。実際のマウスやトラックパッドでは、ブラウザが「方向」を見て次の吸着位置へ進めることがあるため、結果が変わる場合があります。fullPage.js が確実に1画面ずつ進むのに対し、scroll-snap はブラウザと入力機器に任せる部分が残る、というのが手元で見えた違いです。scroll-snap の基本と横方向の作り方は、関連記事「CSS scroll-snapで横スクロールカルーセル|JSなしの完成版」で扱っています。
どちらを選ぶかの判断表
| 状況 | 向くもの | 理由 |
|---|---|---|
| ソースを GPLv3 で公開できない・費用をかけない | CSS scroll-snap | ライセンスの条件が無い |
| ホイール1回で必ず1画面進めたい | fullPage.js | 入力の勢いに関係なく1画面ずつ |
| 移動の前後で演出・解析を動かしたい | fullPage.js | onLeave・afterLoad がそろっている |
| 文章が多く、読み物として読ませたい | ふつうのスクロール | 吸着が読む邪魔になる |
| スマホが中心 | scroll-snap か responsiveWidth | 小さい画面での吸着は好みが分かれる |
| スライドを多用するプレゼン風のページ | fullPage.js | 縦横の移動・URL・ナビゲーションが一式そろう |
| 軽さ・アクセシビリティを優先 | CSS scroll-snap | 追加の JS 無し・Tab の順番が素直 |
JavaScript でスクロール位置を取る・動かす基本は、関連記事「JavaScriptスクロール位置の取得と移動|scrollTo・イベント早見表」で扱っています。要素が画面に入ったときにふわっと表示する演出は、1画面ずつの移動とは別の話題で、IntersectionObserver や WOW.js などで作ります。
よくあるエラーとつまずき(fullPage.jsが動かない)
この節では、fullPage.jsが動かないときに出るエラーの原文と、原因・直し方が分かります。
まずブラウザの開発者ツールのコンソールを開き、出ている文言で下の表を引きます。文言が出ないのに動かないときは、CSS の読み込みとラッパーの位置を疑います。
| 症状(コンソールの原文) | 原因 | 直し方 |
|---|---|---|
Fullpage.js requires a `licenseKey` option. |
licenseKey を書いていない | ライセンスのキーを licenseKey に入れる |
Incorrect `licenseKey`. |
キーの写し間違いなど、正しくないキー | 購入時に発行されたキーをそのまま入れ直す |
Cannot read properties of null (reading 'length') |
head の中で初期化した・セレクターの綴り違い('#fp' など) |
ラッパーより後ろで呼ぶ・id を合わせる |
Cannot read properties of undefined (reading 'call') |
new fullpage('body') と body をラッパーにした |
id を持つ要素で囲む |
$ is not defined |
jQuery 版の書き方なのに jQuery が無い | new fullpage() に書き換える |
Fullpage.js can only be initialized once and you are doing it multiple times! |
2回初期化した(SPA の再描画など) | 先に destroy('all') を呼ぶ |
data-anchor tags can not have the same value as any `id` element on the site と "about" is is being used by another element `id` property |
anchors の名前とページ内の id が同じ | anchors か id の名前を変える |
Options scrollBar:true and autoScrolling:false are mutually exclusive with scrollOverflow:true. |
scrollBar か autoScrolling:false と scrollOverflow を併用 | scrollOverflow を使うなら scrollBar: false |
エラーが出ないのに動かないとき
| 症状 | 原因(手元で再現した内容) | 直し方 |
|---|---|---|
| セクションが全画面にならず、ホイールで何も動かない | CSS を読み込んでいない。セクションの高さは18px のまま、内部の番号だけ進んだ | fullpage.min.css を読み込む |
| 固定ヘッダーが2画面目から消える | ヘッダーをセクションの中に置いた(位置が -800px に) | ラッパーの外に出す |
| 戻るボタンで前のページに戻れない | recordHistory が既定で true | recordHistory: false |
| スマホで長い文章が読みにくい | 小さい画面でも1画面ずつ吸着する | responsiveWidth で切り替える |
| 動きを減らす設定でもアニメーションする | prefers-reduced-motion を見ていない | scrollingSpeed を0にする |
| Tab でメニューに届かない | Tab をライブラリが受け取って表示中のセクションへ送る | スキップリンク・Tab で一周して確かめる |
| 以前の記事のコードが動かない | 2系(jQuery)の書き方・addSection など4系に無いメソッド | 4系の README のメソッド名に合わせる |
DOCTYPE を書かないページ(document.compatMode が BackCompat)でも手元では動きましたが、README は DOCTYPE を必須としています。先頭の <!DOCTYPE html> は省きません。
- 1コンソールにライセンスのエラーが出ていない公開するページでは、条件に合うキーが入っていることを確かめる
- 2固定メニューはラッパーの外にある中に置くと transform の影響で一緒に動く
- 3anchors の名前がページ内の id と重ならない重なるとエラーが2行出る
- 4戻るボタンの動きを決めたセクションごとに戻すか、recordHistory: false にするか
- 5スマホ実機・Tab だけ・動きを減らす設定で一周した手元の自動操作で拾えない差は実機で確かめる
導入前に考える順番をまとめると、次のようになります。
- 1ライセンスの条件を満たせるか満たせなければ CSS scroll-snap へ
- 2中身が1画面に収まるか収まらないセクションが多いなら、ふつうのスクロールも検討
- 3スマホでも吸着させるかさせないなら responsiveWidth
- 4戻るボタン・URL の扱いanchors・recordHistory・lockAnchors
- 5キーボードと動きを減らす設定スキップリンク・scrollingSpeed: 0
fullPage.jsのよくある質問
この節では、fullPage.jsについてよく調べられる疑問への答えが分かります。
Q. fullPage.jsは無料で使えますか?
3系以降は、自分のプロジェクトも GPLv3 で公開する場合に無料で使えます。ソースを公開しない会社のサイトや受託案件では商用ライセンス(2026年10月29日時点で年15米ドルから)が要ります。
Q. licenseKey を入れないとどうなりますか?
手元では、コンソールに licenseKey を求めるエラーが出て、最後のセクションの右下に「Made with fullPage.js」のリンクが表示されました。動きは止まりませんでしたが、ライセンス上の扱いは別で、条件に合うキーを入れて使います。
Q. jQuery は必要ですか?
3系以降は不要です。4.0.41 では、jQuery を先に読み込めば $('#fullpage').fullpage() の形でも初期化できました。
Q. 最新の版はいくつですか?
2026年10月29日時点の npm の最新は 4.0.41(2026年3月4日公開)です。
Q. スクロールの速さを変えるには?scrollingSpeed(ミリ秒、既定700)を変えます。後からは setScrollingSpeed() で変えられ、0・300・1200 で試すと、移動の時間はそれぞれ1・301・1200ミリ秒でした。
Q. 特定のセクションへリンクで飛ばしたいです。anchors を設定すると、#works や #works/2(3枚目のスライド)のような URL で直接開けます。JavaScript からは fullpage_api.moveTo('works', 2) です。
Q. スマホだけふつうのスクロールにできますか?responsiveWidth: 768 のように幅を指定すると、それより狭い画面ではふつうのスクロールになりました。afterResponsive で切り替えを受け取れます。
Q. ページの途中だけ fullPage.js にできますか?
README の autoScrolling: false と fitToSection を組み合わせる方法や、normalScrollElements で一部の要素だけふつうにスクロールさせる方法があります。ページの一部だけに吸着が欲しいなら、その部分だけ CSS scroll-snap にするほうが単純です。
Q. SEO に悪い影響はありますか?
セクションの中身は最初から HTML に入っているので、検索エンジンが読めないわけではありません。ただし URL のハッシュ(#service)は別のページとしては扱われないため、セクションごとに検索から集客したいなら、ページを分けるほうが向きます。
Q. React や Vue で使えますか?
README から React・Vue・Angular 用の公式ラッパーが案内されています。画面を作り直すときは2回初期化のエラーが出やすいので、離れるときに destroy('all') を呼びます。
まとめ
この節では、fullPage.jsの使い方の要点が5つに整理して分かります。
- fullPage.jsは、画面1枚ずつのセクションをホイール・キー・スワイプ1回で切り替えるライブラリで、最新は4.0.41。3系以降は jQuery 不要です。
- ライセンスはGPLv3か商用。ソースを公開しないサイトでは商用ライセンスの licenseKey を入れます。無いとコンソールのエラーと「Made with fullPage.js」が出ました。
- コールバックは beforeLeave → onLeave →(700ms)→ afterLoad の順。onLeave で false を返すと移動を止められます。
- 戻るボタンは既定でセクションごとに戻り、Tab は表示中のセクションへ送られ、動きを減らす設定は見ていません。recordHistory・スキップリンク・scrollingSpeed: 0 で手当てします。
- ライセンスや軽さを優先するなら CSS scroll-snap。ただし合成のホイールでは小さな回転で元に戻るなど、入力機器に任せる部分が残ります。
参考・出典
この節では、この記事で確かめた公式の情報源が分かります(確認日 2026年10月29日)。
- fullPage.js README(GitHub):https://github.com/alvarotrigo/fullPage.js/blob/master/README.md
- fullPage.js 料金ページ:https://alvarotrigo.com/fullPage/pricing/
- fullPage.js licenseKey の説明:https://alvarotrigo.com/fullPage/docs/#licensekey
- npm fullpage.js:https://www.npmjs.com/package/fullpage.js
- MDN scroll-snap-type:https://developer.mozilla.org/ja/docs/Web/CSS/scroll-snap-type
- MDN prefers-reduced-motion:https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion
- MDN Intersection Observer API:https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「fullPage.jsの使い方|ライセンス・動きを実測しscroll-snapと比較」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。