前提
環境
macOS Monterey 12.0.1
Safari 15.1
何をしたかったか
<input type="date">
を利用して、日付の入力欄を作りたかった。
また、 validity
を利用して、不正な値の場合はチェックを行いたかった。
サンプルコード
再現用のコードを作成しました。
See the Pen date bug in Safari by Noboru ISHIKURA (@noboru-i) on CodePen.
何があったか
正常な動作を行うブラウザ(Chromeの場合)
まず、普通にできた Chrome 95 での動作です。
不正な日付を入力して、 validity
の値を確認します。
このように、 value
は空文字が返ってくるので実際に何が入力されたかわからないのですが、
badInput
が true
になっているので、日付として不正な値が入っていることがわかります。
( valid
も false
になっていますね。)
想定外の動作を行うブラウザ(Safariの場合)
次に、問題の Safari 15.1 での動作です。
同じように、不正な日付を入力して validity
の値を確認します。
value
が空文字なのは変わらないのですが、
badInput
が false
です。
これでは、「ユーザーが入力しなかった」のか、「不正な値を入力した = 何か値を入力したつもり」なのかが判断できません。
さらに困ったこと(Safariの場合)
試しに、 value
属性で初期値(2021-01-01
)を与えた状態で、同様に不正な日付を入力してみます。
( value="2021-01-01"
を input
タグの属性として追加します)
この場合も、 Chrome 95 では先程と同じ動作になりました。
Safari 15.1 では以下のようになりました。
何故か、 value
で取得した値が、初期値として与えていた "2021-01-01" となってしまいました。
( badInput
は false
で、 valid
が true
なのは変わらず)
これでは、「別の値を入力したつもり(誤って不正な日付になっているけど)が、初期値で送信されてしまう」ことになってしまいます。
どうするか(解決策?)
Safari では validity
を見ても意味がなさそうなので、 value
に「入力されないはずの値」を入れた上で、画面読み込み後に書き換えてみます。
See the Pen workaround bug in Safari by Noboru ISHIKURA (@noboru-i) on CodePen.
まずは Chrome 95 での結果。
validity.badInput
から判別できています。
次に問題の Safari 15.1 での結果。
value
属性をそのまま出力している rawValue:
の部分は初期値で与えたものが出てしまっていますが、それと一致していることを判定することで、不正な入力であることがわかります。
コードとしては、以下のように判断しています。
(プロダクションコードでは、必須や範囲などの他のチェックも必要だと思いますが、割愛。)
function getCheckResult(input) {
if (input.validity.badInput) {
return "(bad input)"
}
if (input.value == "1900-01-01") {
return "(bad input on Safari)";
}
if (input.value == "") {
return "(empty)";
}
return "(OK)";
}
感想
普段は、AndroidやiOSのモバイルアプリを実装しているので、「最近のブラウザって、 input type="date"
とかのコンポーネントが増えてきて楽になっているんだろう」と思っていましたが、 Safari はやっぱりくせ者でした。
今回、「入力されないはずの値」を使うことでチェックしましたが、「どんな値でも入力されるはずの入力欄」では利用できない手法かと思います。
こんなとき、皆さんはどうするんでしょうか?
そもそも、日付入力ライブラリなどを利用する?実はいい感じの対処法がある?
ご存じの方がいれば、教えて下さい 🙏🙏