<input type="date">の初期値に現在日付を設定する

2024/03/04に公開

概要

<input>要素のtype="date"とは、日付入力のためのフォームです。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/date

日付というものは厄介で「現在日付を初期値とする」としたくても時々刻々と設定すべき初期値が変わってしまうため、HTMLだけでは実現できません。
すなわちJavaScriptを用意する必要があるのですが、どうすればシンプルに実装できるでしょうか。

ターゲット

  • 初期表示時、<input type="date">に現在の日付など動的な日付を設定したい方

ゴール

  • <input type="date">に動的な日付を設定できるようになる

結論、サンプルコード

私の結論はこちら。カスタム要素で実現します。

現在日付を設定する処理
class InputDateDefault extends HTMLInputElement {
    connectedCallback() {
        let now = new Date();
        this.value = now.toLocaleDateString('sv-SE');
    }
}
customElements.define('default-date', InputDateDefault, { extends: 'input', });

HTML上で使用するときは、上記を<script>で読み込んだうえで<input>要素にis属性を設定します。
is属性の値は、defineの第一引数と同じ値を設定しましょう。

<input type="date" is="default-date" />

解説

今回のポイントは「カスタム要素」と「toLocaleDateString」です。

カスタム要素

DOMContentLoadedでHTMLが読み込まれた瞬間に<input type="date">へ初期値を設定するというスクリプトを組むことも可能です。

DOMContentLoadedで初期値を設定する例
document.addEventListener('DOMContentLoaded', _ => {
    document.querySelectorAll('[type=date]').forEach(element => {
        let now = new Date();
        element.value = now.toLocaleDateString('sv-SE');
    });
});

ですがDOMContentLoadedは画面を表示した瞬間にのみ実行されるので、動的に画面を更新するシングルページアプリケーションなどとは相性が悪いです。

なので手っ取り早くカスタム要素で、<input>タグそのものを拡張しちゃいます。
カスタム要素とは「指定した要素のDOM操作があった場合に実行されるスクリプトを組み込むことができる」機能です。

カスタム要素については、おなじみ「とほほのWWW入門」が詳しいです。
https://www.tohoho-web.com/ex/custom-elements.html

やることはシンプルで「<input type="date">が追加された際に実行する」と書くだけ。

現在日付を設定する処理
// HTMLInputElement(input要素)を拡張する
class InputDateDefault extends HTMLInputElement {
    // HTMLに該当の要素を追加する場合に実行する
    connectedCallback() {
        // this(追加する要素)に日付を設定する
        let now = new Date();
        this.value = now.toLocaleDateString('sv-SE');
    }
}

// is="default-date"をつけたinput要素に適用するよう登録する
customElements.define('default-date', InputDateDefault, { extends: 'input', });

応用編として、例えばdiff属性で数字を指定している場合はその日数だけ進めた初期値にするなんてことも可能です。

応用編
class InputDateDefault extends HTMLInputElement {
    connectedCallback() {
        let now = new Date();
        // this.attributes.diff でdiff属性の値を取得する
        // ※実際には diff属性の有無などの判定が必要
        now.setDate(now.getDate() + Number(this.attributes.diff.value));
        this.value = now.toLocaleDateString('sv-SE');
    }
}
customElements.define('default-date', InputDateDefault, { extends: 'input', });
応用編の場合のHTML(昨日の日付が設定されます)
<input type="date" is="default-date" diff="-1" />

toLocaleDateString

<input type="date">value属性はYYYY-MM-DD書式で設定する必要があります。
微妙に厄介なことに、ゼロを省いた2024-3-4という値は許されず、2024-03-04と丁寧に描く必要があります。厄介なことに。

そこで今回、すこし特殊な実装として日付のフォーマット書式をsv-SE(sv: スウェーデン語、SE: スウェーデン王国)に合わせています。

スウェーデン語の日付書式で出力する
let now = new Date();
console.info(now.toLocaleDateString('sv-SE'));

面白いことに、スウェーデンの日付書式はYYYY-MM-DDで定義されているらしく、手軽にYYYY-MM-DD書式で取得するハック術としてstack overflowで紹介されています。
https://stackoverflow.com/questions/25050034/get-iso-8601-using-intl-datetimeformat#answer-58633686

ただし言語に対応した値を取得する方法はプログラミング言語の仕様やそれぞれの国の法律に振り回される傾向があります。
他のプログラミング言語Javaで実際にあったことですが、jdk-8u161からjdk-8u171にマイナーバージョンアップがされた際に日付型宣言にサマータイムが考慮されるようになったりもしました。
https://qiita.com/m_mouri/items/10bd97f0dd38a966bce2

ローカライズ関連の処理で実装するのがあまり適切ではないと考えられる場合は、下記の通り地道にYYYY-MM-DD書式を作っていきます。

地道に取得する
let now = new Date();
let year = now.getFullYear();
let month = String(now.getMonth() + 1).padStart(2, '0');
let day = String(now.getDate()).padStart(2, '0');
console.info(`${year}-${month}-${day}`);

あるいはフォーマット文字列を使うのもよいでしょう。(メモリを食うらしい情報もありますが)

フォーマット文字列
// new Intl.DateTimeFormatはメモリを食うらしいので何度も呼び出されないように注意し実装する
const FORMAT_YYYYMMDD = new Intl.DateTimeFormat(
    undefined,
    {
        year:   'numeric',
        month:  '2-digit',
        day:    '2-digit',
    }
)
let now = new Date();
console.info(FORMAT_YYYYMMDD.format(now).replaceAll('/', '-'));

https://zenn.dev/mazamachi/articles/js-intl-date-time-format-performance

とはいえ

ここまで解説しておいてなんですが「ユーザーが入力した値ではない値」を初期表示するのは、個人的にはあまり好みではありません。

動的な日付が必要ならば「フォームを受け取った側で現在日付を補い、行ったことを通知する」か、「必須入力とする」、あるいは「初期表示時にViewとして出力する」方法にしたほうが、フロントサイドでの余計な制御も減りますし、テストもサーバーサイドの責任にすることができます。

今回の策はあくまでフロントだけで解決する必要がある場合の緊急策、といったところでしょうか。

Discussion