flatpickrで日付の範囲選択を実装する
方法【動くデモ付き】

結論:1フィールドか2フィールドかで実装が変わる

flatpickrで期間指定を実装する方法は大きく2つあります。1つの入力欄に「開始日〜終了日」をまとめて表示するmode: "range"と、開始日用・終了日用のinputを別々に用意して連動させる2フィールド構成です。どちらも動くコードを載せます。

パターン1: mode: "range"(1フィールド)

<input type="text" id="range-picker" placeholder="開始日 〜 終了日">

<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.js"></script>
<script>
  flatpickr("#range-picker", {
    mode: "range",
    dateFormat: "Y-m-d"
  });
</script>

実際に動くデモです。開始日をクリックしたあと終了日をクリックすると、その間の日付がハイライトされます。

パターン2: 開始日・終了日を2フィールドで連動

<input type="text" id="start-picker" placeholder="開始日">
<input type="text" id="end-picker" placeholder="終了日">

<script>
  var startPicker = flatpickr("#start-picker", {
    dateFormat: "Y-m-d",
    onChange: function(selectedDates) {
      endPicker.set("minDate", selectedDates[0]);
    }
  });

  var endPicker = flatpickr("#end-picker", {
    dateFormat: "Y-m-d"
  });
</script>

こちらも動くデモです。開始日を選ぶと、終了日側のカレンダーで開始日より前の日付が選べなくなります。

どちらを選ぶか

個人向けの予約フォームのように「期間」という1つの概念として扱いたい場合は、入力欄が1つで済むmode: "range"のほうが実装が軽く、UIもシンプルです。宿泊予約やイベント申し込みのようなユースケースに向いています。

一方、業務アプリの検索条件フォームでは、開始日と終了日を別々のカラムとして扱うことが多く、2フィールド構成のほうが需要が高くなります。片方だけをクリアしたい、終了日だけをバリデーションしたいといった個別の制御がしやすいのは、入力欄が分かれている構成のほうです。「範囲選択ならmode: "range"一択」と決めつけず、あとで検索条件やフィルター処理と組み合わせる予定があるなら、最初から2フィールドで作っておいたほうが手戻りが少なくなります。

mode: "range"

入力欄1つで完結し、実装量が少ない。取得できる値はselectedDatesの配列(開始日・終了日の2要素)で、開始日と終了日を別々のフィールドに分離して送信したい場合は自分で分解する必要がある。

2フィールド連動

開始日用・終了日用のinstanceをそれぞれ持つため、片方だけの値取得やクリアが素直に書ける。検索条件フォームや帳票の期間指定など、業務系の画面と相性がよい。

よくあるバリエーション・ハマりどころ

終了日が開始日より前を選べてしまう

2フィールド構成で一番多い抜けが、終了日側にminDateの制限をかけ忘れるケースです。開始日を選んだタイミングのonChangeで、終了日側のインスタンスにset("minDate", ...)を呼ぶことで、開始日より前の日付をカレンダー上でグレーアウトできます。

var startPicker = flatpickr("#start-picker", {
  dateFormat: "Y-m-d",
  onChange: function(selectedDates) {
    if (selectedDates[0]) {
      endPicker.set("minDate", selectedDates[0]);
    }
  }
});

逆に、終了日を先に選んで開始日をあとから変更するケースまで考慮するなら、開始日側にも終了日を上限とするmaxDateを同様に設定しておくと、前後関係が崩れる操作を防げます。

mode: "range"でselectedDatesの要素数が1のまま止まる

mode: "range"では、開始日をクリックした直後はselectedDatesの要素数が1です。終了日を選び終えるまでは範囲が確定していないため、要素数が2になったタイミングで初めて期間が確定したと判定する必要があります。1件しかない状態を「範囲選択が完了した」と誤判定すると、単一の日付だけで処理を進めてしまうバグになります。

flatpickr("#range-picker", {
  mode: "range",
  dateFormat: "Y-m-d",
  onChange: function(selectedDates, dateStr) {
    if (selectedDates.length === 2) {
      // ここで初めて開始日・終了日が両方確定
      console.log("開始日:", selectedDates[0], "終了日:", selectedDates[1]);
    }
  }
});

確定タイミングの引数の意味は「flatpickrのonChangeで選択した日付を取得する方法」で詳しく解説しています。

日本語表示にすると曜日がわかりにくい

期間指定は選択期間が長くなるほど、開始日・終了日の曜日を意識する場面が増えます。locale: "ja"dateFormatに曜日書式を組み合わせておくと、入力欄を見ただけで曜日まで確認できます。設定方法は「flatpickrを日本語化する方法」を参照してください。

まとめ

flatpickrの範囲選択は、入力欄をまとめたいならmode: "range"、開始日・終了日を独立して制御したいなら2フィールド連動という使い分けになります。業務アプリの検索条件のような用途では、あとからの拡張性を考えて2フィールドを選んでおくほうが無難です。これでflatpickrの日本語化・値取得・範囲選択の3本がひととおり揃ったので、実装の際は困ったところから読み返してもらえればと思います。

関連するUI事例

flatpickrを使った日付・時刻選択の実装例です。単一選択・範囲選択・時刻選択のパターンをまとめて確認できます。