グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



Safariでのみ正常に動かない!?iPhoneでdatePickerが正常に動かなくてハマったお話

みなさま、javascript使っていますでしょうか。
筆者はPHPをメインでシステム開発を行っており、補助的に使うことが多いです。

そんな筆者が今回ぶち当たった壁と、その解決方法について解説していこうと思います。

開発環境

  • ブラウザ(PC):Chrome最新版(2024/01/29時点)
  • ブラウザ(iPhone):safari最新版(2024/01/29時点)
  • Vue:3.3.8
  • Vue datepicker:3.6.5

発生した事象

お問い合わせフォーム構築時、日付の入力箇所にVue datepickerを用いてdatepickerを実装しましたが、iPhone(Safari)の時だけ当日選択しかできなくなってしまいました。

▼ PCだと問題ない

▼ SP(iPhone)だと当日しか選択できない!

原因の調査

現在のコードを見ると、下記の仕様のためVue datepickerのオプションをたくさん設定していた。
原因切り分けのためにオプションを削除しながら動作確認をしてみる。

  • 選択可能なのは3日前~10日の間のみ
  • 土日は選択不可
  • 見た目調整やtimepickerの非アクティブ化
<table>
    <tr>
        <th class="title"><span>日付選択</span></th>
        <td>
            <datepicker
                    v-model="selectedDate"
                    :min-date="minDate"
                    :max-date="maxDate"
                    :disabled-week-days="[0,6]"
                    :enable-time-picker="false"
                    :auto-apply="true"
                    format="yyyy-MM-dd"
                    model-type="yyyy-MM-dd"
                    week-start="0"
                    locale="ja"
                    placeholder="日付を選択"
            ></datepicker>
        </td>
    </tr>
</table>
const app = Vue.createApp({
    data() {
        return {
            selectedDate: '',
        }
    },
    delimiters: ['${', '}'],
    components: {
        datepicker: VueDatePicker
    },
    computed: {
        minDate: function () {
            let now = new Date();
            now.setDate(now.getDate()-3);
            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
        },
        maxDate: function () {
            let now = new Date();
            now.setDate(now.getDate()+10);
            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
        },
    }
}).mount('#app');

まずは、見た目調整のオプションを削除

まずは手ごろなところで見た目のオプションから削除して動作確認をしてみます。

$ git diff
                    :min-date="minDate"
                    :max-date="maxDate"
                    :disabled-week-days="[0,6]"
-                   :enable-time-picker="false"
-                   :auto-apply="true"
-                   format="yyyy-MM-dd"
-                   model-type="yyyy-MM-dd"
-                   week-start="0"
-                   locale="ja"
-                   placeholder="日付を選択"
            ></datepicker>
        </td>
    </tr>

結果は… 変わらない。

次に、土日選択不可のオプションを削除

土日選択不可のオプションを削除して動作確認をしてみます。

$ git diff
                    v-model="selectedDate"
                    :min-date="minDate"
                    :max-date="maxDate"
-                   :disabled-week-days="[0,6]"
                    :enable-time-picker="false"
                    :auto-apply="true"
                    format="yyyy-MM-dd"

こちらも変わらない…

最後に、最小値最大値のオプションを削除

最小値最大値のオプションを削除して動作確認をしてみます。

$ git diff
        <td>
            <datepicker
                    v-model="selectedDate"
-                   :min-date="minDate"
-                   :max-date="maxDate"
                    :disabled-week-days="[0,6]"
                    :enable-time-picker="false"
                    :auto-apply="true"

ついに、本日以外もアクティブになりました!

悪さをしていたのはmin-dateオプションとmax-dateオプションと判明したので、該当オプションのDocumentを見てみます。

引数はDate型でも今回渡しているstring型でも問題はなさそうです。

公式ドキュメント

ここで、Vue datepickerが原因ではないのではないかと思い、iPhoneのsafariとDateObjectについて調べていると、気になる記事をいくつか発見しました。

記事の情報によると、safariはハイフンつなぎの日付(例:2024-01-29)、0埋めでない日付(例:2024/1/29)を日付フォーマットとして解釈してくれないようです。

修正と検証

min-datemax-dateを作っているコードを見てみると、返り値はハイフンつなぎかつ0埋めでないstring(2024-1-29)になっていました。

        minDate: function () {
            let now = new Date();
            now.setDate(now.getDate()-3);
            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
        },
        maxDate: function () {
            let now = new Date();
            now.setDate(now.getDate()+10);
            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
        },

ここをスラッシュつなぎ、かつ0埋めのstring(2024/01/29)を返すように作り変えてみました。

$ git diff
const app = Vue.createApp({
         minDate: function () {
             let now = new Date();
             now.setDate(now.getDate()-3);
-            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
+            return now.getFullYear()+'/'+("0"+(now.getMonth()+1)).slice(-2)+'/'+("0"+now.getDate()).slice(-2);
         },
         maxDate: function () {
             let now = new Date();
             now.setDate(now.getDate()+10);
-            return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
+            return now.getFullYear()+'/'+("0"+(now.getMonth()+1)).slice(-2)+'/'+("0"+now.getDate()).slice(-2);
         },
     }
 }).mount('#app');

結果は…

PCは変わらず正常に動作していることを確認!

iPhone(Safari)でも正常に動作していることを確認!!

まとめ

javascriptの様なクライアントサイドスクリプトはブラウザによって挙動が異なることがあり、思わぬ落とし穴にはまることがあるなと痛感しました。
また今回の場合はハイフンつなぎで返り値を実装していたため、いつ動作チェックを行っても動作不良を起こしていたかもしれないが、初期実装がスラッシュつなぎの0埋めなし(例:2024/1/29)で実装していた場合、月も日付も2桁の日に動作テストを行っていたら問題なく通っていたと考えられる…とても恐ろしい…

javascriptでの日付周りの実装にDateObjectを使用する際は是非注意して実装してください。
また上記実装の参考になれば非常に幸いです。

この記事を書いた人

ばね
ばねソリューション事業部 システムエンジニア
東京で2年半エンジニアとしての経験を積み、浜松にUターンの後、アーティスへ入社。
ソリューション事業部のWebエンジニアとして、システムの設計・開発・保守・運用からインフラまで幅広く従事している。
フルスタックエンジニア目指して現在も勉強の日々。車が好き。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします