本記事では、フォーム入力の進捗度を可視化するプログレスバーの実装方法を紹介します。
プログレスバーを設置するだけで、入力の現在地がひと目で分かり、離脱抑止と完了率向上に寄与します。軽量で既存フォームに簡単に組み込み可能です。
ポイント: 視覚的な進捗提示は入力ストレスの低減に有効
導入効果
- 完了率の見える化でモチベーション維持
- 入力体験の向上(スムーズなアニメーション)
- ページ上部に固定され常に視認可能
- 軽量・依存なしで既存フォームにすぐ追加
- 完了率の見える化でモチベーション維持
- 入力体験の向上(スムーズなアニメーション)
- ページ上部に固定され常に視認可能
- 軽量・依存なしで既存フォームにすぐ追加
できること(メリット詳細)
デモサイトで動作をご確認頂けます。
テキストを入力してみたり、ラジオボタン、チェックボックス等を選択したりして動作をご確認ください。
- 離脱抑止: 目標達成までの距離が明確になり完了率向上が期待
- UX向上: スムーズな変化と演出で入力体験がリッチに
- 常時可視: 上部固定により入力中も進捗を見失わない
- 導入容易: 既存フォームにHTML/CSS/JSを追加するだけ
- ブランド適合: 色や動きを簡単にブランドトーンへ調整可能
コードの説明と実装方法
プログレスバーのCSS
/* CSS ここから */ :root { /* 背景色(白) */ --background: #FFFFFF; /* 枠線色(薄いグレー) */ --border: #B3B3B3; /* プログレスバーのグラデーション色(明るい側/濃い側) */ --gradient-light: #00a1eb; --gradient-dark: #00ffd9; } .progress-bar-area { padding: 10px 18px 6px 18px; background-color: var(--background); border-bottom: 2px solid var(--border); } .progress-bar-container { background-color: var(--background); overflow: hidden; margin: 8px auto; border: 1px solid var(--border); border-radius: 10px; } .progress-bar { height: 10px; width: 0%; /* グラデーションの色は :root の --gradient-light / --gradient-dark を変更 */ background: linear-gradient( to right, var(--gradient-light) 0%, var(--gradient-dark) 25%, var(--gradient-light) 50%, var(--gradient-dark) 75%, var(--gradient-light) 100% ); background-size: 200% 100%; background-position: 0% 0%; /* 下記のアニメーション速度は JS 側でも上書きできます */ animation: gradient-move 8s linear infinite; transition: width 1s ease; /* 伸縮速度は JS 側でも上書きできます */ position: relative; overflow: visible; border-radius: 10px; } @keyframes gradient-move { 0% { background-position: 0% 0%; } 100% { background-position: 100% 0%; } } .fixed-progress { position: fixed; top: 0; z-index: 1000; width: calc(100% - 36px); } /* CSS ここまで */
実装方法
CSSはページ内の
PC版とスマートフォン版でCSSを分ける必要はありません。
ただし、スマートフォン版のCSSを追加する場合は、PC版のCSSよりも下に記載してください。
<style>タグ内に記載してください。
PC版とスマートフォン版でCSSを分ける必要はありません。
ただし、スマートフォン版のCSSを追加する場合は、PC版のCSSよりも下に記載してください。
プログレスバーのHTML
<div class="progress-bar-area"> <span>進捗度: </span> <span id="progress-text"></span> <div class="progress-bar-container"> <div class="progress-bar" id="progress-bar"></div> </div> </div>
実装方法
このHTMLを使用して進捗度バーを表示します。
<body class="body">タグと
<center>タグの間に記載してください
プログレスバーを制御するJavascript
<script> (function () { 'use strict'; const SETTINGS = { // バーの幅が変わるトランジション時間(ミリ秒) TRANSITION_MS: 1000, // 背景グラデーション(CSS keyframes: gradient-move)の再生時間(ミリ秒) GRADIENT_ANIM_MS: 8000, // 伸縮方向に応じて付与するリップル演出の長さ(ミリ秒) RIPPLE_MS: 700, // スクロールがこの値以上で固定クラスを付与 FIXED_SCROLL_THRESHOLD: 1, // 固定表示する際の上部オフセット(px)。必要に応じて変更 FIXED_TOP_PX: 0, }; // ============================================================= // 定数 // ============================================================= const INPUT_SELECTOR = 'input, textarea, select'; const PROGRESS_BAR_ID = 'progress-bar'; const PROGRESS_TEXT_ID = 'progress-text'; const PROGRESS_CONTAINER_SELECTOR = '.progress-bar-area'; // ------------------------------------------------------------- // ユーティリティ // ------------------------------------------------------------- function getGroupName(name) { return name.split(':')[0]; } function isFieldFilled(element) { if (element.type === 'text' || element.type === 'password' || element.tagName === 'TEXTAREA') { return element.value.trim() !== ''; } if (element.tagName === 'SELECT') { return element.value !== ''; } return false; } function collectVisibleFormElements() { return Array.from(document.querySelectorAll(INPUT_SELECTOR)).filter(el => el.offsetParent !== null && el.type !== 'hidden' && el.type !== 'submit' && el.type !== 'button' && el.type !== 'reset' ); } function groupFieldsByName(elements) { const groups = {}; elements.forEach(el => { const name = el.getAttribute('name'); if (!name) return; if (el.type === 'hidden') return; const group = getGroupName(name); if (!groups[group]) groups[group] = []; groups[group].push(el); }); return groups; } function computeProgress(groups) { let completedGroups = 0; let totalGroups = 0; Object.values(groups).forEach(groupFields => { const visibleFields = groupFields.filter(el => el.type !== 'hidden'); if (visibleFields.length === 0) return; totalGroups++; const isRadioOrCheckbox = visibleFields.some(el => el.type === 'radio' || el.type === 'checkbox'); if (isRadioOrCheckbox) { if (visibleFields.some(el => el.checked)) { completedGroups++; } } else { if (visibleFields.every(isFieldFilled)) { completedGroups++; } } }); // グループが0件のときは 0% を返す(初期表示でNaN%にならないように) const percent = totalGroups > 0 ? (completedGroups / totalGroups) * 100 : 0; return { completedGroups, totalGroups, percent }; } function renderProgress(progress) { const progressBar = document.getElementById(PROGRESS_BAR_ID); const progressText = document.getElementById(PROGRESS_TEXT_ID); if (!progressBar || !progressText) return; progressBar.style.width = progress.percent + '%'; progressText.innerText = Math.round(progress.percent) + '%'; console.log(progress.completedGroups); console.log(progress.totalGroups); } // ------------------------------------------------------------- // 付随機能(固定表示/リップルアニメーション) // ------------------------------------------------------------- function setupFixedProgressOnScroll() { window.addEventListener('scroll', function () { const progressBarContainer = document.querySelector(PROGRESS_CONTAINER_SELECTOR); if (!progressBarContainer) return; const scrollTop = window.scrollY; const shouldFix = scrollTop >= SETTINGS.FIXED_SCROLL_THRESHOLD; if (shouldFix) { progressBarContainer.classList.add('fixed-progress'); // 固定時の上端位置を設定(CSSのtopを上書き) progressBarContainer.style.top = SETTINGS.FIXED_TOP_PX + 'px'; } else { progressBarContainer.classList.remove('fixed-progress'); progressBarContainer.style.top = ''; } }); } function setupRippleAnimation() { const progressBar = document.getElementById(PROGRESS_BAR_ID); if (!progressBar) return; // 伸縮速度(transition)を設定 progressBar.style.transition = 'width ' + SETTINGS.TRANSITION_MS + 'ms ease'; // グラデーションの移動速度(CSS変数で渡せないため style.animationDuration を直接設定) // 既存CSS: animation: gradient-move 8s linear infinite; progressBar.style.animationDuration = SETTINGS.GRADIENT_ANIM_MS + 'ms'; let rippleTimeout; let previousWidth = parseFloat(progressBar.style.width) || 0; const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'attributes' && mutation.attributeName === 'style') { const newWidth = parseFloat(progressBar.style.width) || 0; if (newWidth === previousWidth) return; const rippleClass = newWidth > previousWidth ? 'rippling-right' : 'rippling-left'; progressBar.classList.remove('rippling-right', 'rippling-left'); void progressBar.offsetWidth; // Reflow progressBar.classList.add(rippleClass); clearTimeout(rippleTimeout); rippleTimeout = setTimeout(() => { progressBar.classList.remove(rippleClass); }, SETTINGS.RIPPLE_MS); previousWidth = newWidth; } }); }); observer.observe(progressBar, { attributes: true }); } // ------------------------------------------------------------- // 初期化 // ------------------------------------------------------------- function updateProgressBar() { const elements = collectVisibleFormElements(); const groups = groupFieldsByName(elements); const progress = computeProgress(groups); renderProgress(progress); } function init() { document.querySelectorAll(INPUT_SELECTOR).forEach(element => { element.addEventListener('input', updateProgressBar); }); setupFixedProgressOnScroll(); setupRippleAnimation(); updateProgressBar(); } window.addEventListener('load', init); })(); </script>
実装方法
<body class="body">タグ内の一番下に設置してください。
カスタマイズ(色・動きの変更ガイド)
ここでは、CSSと JavaScript の「先頭の変数」だけを変えることで、 見た目の色やアニメーションの速さ、固定位置を簡単に調整する方法を説明します。
ポイント
「先頭の変数だけ」を変える設計なので、プログラムの知識がなくても安心して調整できます。
「先頭の変数だけ」を変える設計なので、プログラムの知識がなくても安心して調整できます。
1. 色の変更
次の4つの項目を変更すると、見た目の色が一括で切り替わります。
:root { --background: #FFFFFF; /* 背景の色(白) */ --border: #B3B3B3; /* 枠線の色(薄いグレー) */ --gradient-light: #00a1eb; --gradient-dark: #00ffd9; }
- バーの色を変えたい:
--gradient-light
と--gradient-dark
をお好みの2色に。 - 単色にしたい: バーのCSS内の
background: linear-gradient(...)
をbackground-color: 好きな色;
に変更。 - 枠線の色:
--border
を変更。 - 背景の色:
--background
を変更。
色の書き方は #RRGGBB
(例: #16A34A
)や、色名(例: red
)でもOKです。
2. 動き(速さ・固定位置)の変更
次の項目を変えるだけで、アニメーションの速さや固定位置を調整できます。
const SETTINGS = { TRANSITION_MS: 1000, // バーが伸び縮みする速さ(ミリ秒) GRADIENT_ANIM_MS: 8000,// 背景の流れる速さ(ミリ秒) RIPPLE_MS: 700, // 伸縮時の演出の長さ(ミリ秒) FIXED_SCROLL_THRESHOLD: 1, // 何pxスクロールで上部固定にするか FIXED_TOP_PX: 0, // 固定するときの上からの位置(px) };
- 伸縮の速さ:
TRANSITION_MS
(数値を小さくすると速くなります) - 背景の流れる速さ:
GRADIENT_ANIM_MS
(小さいほど速い) - 演出の長さ:
RIPPLE_MS
(演出が長く続きます) - 固定の開始位置:
FIXED_SCROLL_THRESHOLD
(この値以上スクロールすると固定) - 固定時の位置:
FIXED_TOP_PX
(ヘッダーがある場合はその高さぶん加算)
3. 作業の流れ(かんたん手順)
- 色を変える:
code3-1
のいちばん上(:root {...}
)の数値を変更 - 動きを変える:
code2-1
のいちばん上(SETTINGS {...}
)の数値を変更 - SPIRALへ反映し、ブラウザで表示を確認
4. うまく変わらない時
- ブラウザをリロードしてください。
【お知らせ】本実装の構築代行・カスタマイズを承ります
記事でご紹介した内容や、貴社の要件に合わせた柔軟なカスタマイズ開発を承っております。
SPIRALの専門チームが要件定義から実装まで一貫してサポートいたしますので、まずはお見積もりをご依頼ください。