<input type="date">の初期値に現在日付を設定する
概要
<input>
要素のtype="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">
へ初期値を設定するというスクリプトを組むことも可能です。
document.addEventListener('DOMContentLoaded', _ => {
document.querySelectorAll('[type=date]').forEach(element => {
let now = new Date();
element.value = now.toLocaleDateString('sv-SE');
});
});
ですがDOMContentLoaded
は画面を表示した瞬間にのみ実行されるので、動的に画面を更新するシングルページアプリケーションなどとは相性が悪いです。
なので手っ取り早くカスタム要素で、<input>
タグそのものを拡張しちゃいます。
カスタム要素とは「指定した要素のDOM操作があった場合に実行されるスクリプトを組み込むことができる」機能です。
カスタム要素については、おなじみ「とほほのWWW入門」が詳しいです。
やることはシンプルで「<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', });
<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で紹介されています。
ただし言語に対応した値を取得する方法はプログラミング言語の仕様やそれぞれの国の法律に振り回される傾向があります。
他のプログラミング言語Javaで実際にあったことですが、jdk-8u161からjdk-8u171にマイナーバージョンアップがされた際に日付型宣言にサマータイムが考慮されるようになったりもしました。
ローカライズ関連の処理で実装するのがあまり適切ではないと考えられる場合は、下記の通り地道に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('/', '-'));
とはいえ
ここまで解説しておいてなんですが「ユーザーが入力した値ではない値」を初期表示するのは、個人的にはあまり好みではありません。
動的な日付が必要ならば「フォームを受け取った側で現在日付を補い、行ったことを通知する」か、「必須入力とする」、あるいは「初期表示時にViewとして出力する」方法にしたほうが、フロントサイドでの余計な制御も減りますし、テストもサーバーサイドの責任にすることができます。
今回の策はあくまでフロントだけで解決する必要がある場合の緊急策、といったところでしょうか。
Discussion