input type="date"を使ったら、MacのSafariで困った話

## 前提

### 環境

macOS Monterey 12.0.1
Safari 15.1

### 何をしたかったか

`<input type="date">` を利用して、日付の入力欄を作りたかった。
また、 `validity` を利用して、不正な値の場合はチェックを行いたかった。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/date

https://developer.mozilla.org/ja/docs/Web/API/ValidityState

### サンプルコード

再現用のコードを作成しました。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="porOmOm" data-user="noboru-i" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/noboru-i/pen/porOmOm">
  date bug in Safari</a> by Noboru ISHIKURA (<a href="https://codepen.io/noboru-i">@noboru-i</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 何があったか

### 正常な動作を行うブラウザ（Chromeの場合）

まず、普通にできた Chrome 95 での動作です。
不正な日付を入力して、 `validity` の値を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/428f5807-25e3-a385-82eb-1b3843cf21ad.png)

このように、 `value` は空文字が返ってくるので実際に何が入力されたかわからないのですが、
`badInput` が `true` になっているので、日付として不正な値が入っていることがわかります。
（ `valid` も `false` になっていますね。）

### 想定外の動作を行うブラウザ（Safariの場合）

次に、問題の Safari 15.1 での動作です。
同じように、不正な日付を入力して `validity` の値を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/f13fbc6e-3a7f-ec08-f3ef-c1cef530e87c.png)

`value` が空文字なのは変わらないのですが、
`badInput` が `false` です。
これでは、「ユーザーが入力しなかった」のか、「不正な値を入力した = 何か値を入力したつもり」なのかが判断できません。

### さらに困ったこと（Safariの場合）

試しに、 `value` 属性で初期値（`2021-01-01`）を与えた状態で、同様に不正な日付を入力してみます。
（ `value="2021-01-01"` を `input` タグの属性として追加します）

この場合も、 Chrome 95 では先程と同じ動作になりました。

Safari 15.1 では以下のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/abc0bb07-789e-7014-9244-c5f936e046f7.png)

何故か、 `value` で取得した値が、初期値として与えていた "2021-01-01" となってしまいました。
（ `badInput` は `false` で、 `valid` が `true` なのは変わらず）
これでは、「別の値を入力したつもり（誤って不正な日付になっているけど）が、初期値で送信されてしまう」ことになってしまいます。

## どうするか（解決策？）

Safari では `validity` を見ても意味がなさそうなので、 `value` に「入力されないはずの値」を入れた上で、画面読み込み後に書き換えてみます。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="PoKdrpV" data-user="noboru-i" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/noboru-i/pen/PoKdrpV">
  workaround bug in Safari</a> by Noboru ISHIKURA (<a href="https://codepen.io/noboru-i">@noboru-i</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

まずは Chrome 95 での結果。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/70581ce1-953b-ac16-a9ca-7283460de494.png)

`validity.badInput` から判別できています。

次に問題の Safari 15.1 での結果。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/31cde1c2-bdf2-727a-890c-f6268e54a992.png)

`value` 属性をそのまま出力している `rawValue:` の部分は初期値で与えたものが出てしまっていますが、それと一致していることを判定することで、不正な入力であることがわかります。

コードとしては、以下のように判断しています。
（プロダクションコードでは、必須や範囲などの他のチェックも必要だと思いますが、割愛。）

```js
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 はやっぱりくせ者でした。

今回、「入力されないはずの値」を使うことでチェックしましたが、「どんな値でも入力されるはずの入力欄」では利用できない手法かと思います。
こんなとき、皆さんはどうするんでしょうか？
そもそも、日付入力ライブラリなどを利用する？実はいい感じの対処法がある？
ご存じの方がいれば、教えて下さい　🙏🙏