在設計輸入日期的表單時,有時我們會需要限定時間範圍。
像是常見的網銀帳戶記錄,很多就有類似從今天開始前的一年內為查詢範圍的限制,有時候就算在說明文字中已說明資料範圍僅有一年內的資料,還是有可能會有用戶去選到超過一年內的日期。
這種時候直接禁止選取超過一年內的日期,可以避免使用者不小心進行錯誤的操作。
要設定 <input type=”date”> 的範圍其實用原生的 JS 就能做到,只需要增加 max 或 min 的屬性即可:
<html>
<head>
<title>Date Range Example</title>
</head>
<body>
<input type="date" id="dateInput">
<script>
// 獲取今日日期
const today = new Date();
// 設定最大值為今日
const maxDate = today.toISOString().split("T")[0];
// 設定最小值為一年前
today.setFullYear(today.getFullYear() - 1);
const minDate = today.toISOString().split("T")[0];
// 設置 input 的最小值和最大值
document.getElementById('dateInput').max = maxDate;
document.getElementById('dateInput').min = minDate;
</script>
</body>
</html>
而在 Vue 中就可以這樣寫:
<template>
<input type="date" :max="maxDate" :min="minDate">
</template>
<script setup>
import { ref, onMounted } from 'vue';
const today = new Date;
const maxDate = ref('');
const minDate = ref('');
function setDateRange() {
maxDate.value = today.toISOString().split("T")[0];
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().split("T")[0];
}
onMounted(() => {
setDateRange();
})
</script>
input date 屬性:max 與 min
max 與 min 都是 <input type=”date”> 本身就擁有的屬性,從 MDN 中的說明可以看到,設定的時候需符合 yyyy-mm-dd 這樣的日期格式。因此如果有一個已知的固定時間,也是可以直接把 max 或 min 的值寫死。
new Date():動態取得今日日期
因為在這個案例中,要取得的日期範圍是「從今日開始到一年前」這個動態的區間,因此可以用new Date() 這個方法來取得現在日期和時間的 Date 物件。
console.log() 之後看到的內容會是:Tue Oct 22 2024 00:00:00 GMT+0800 (台北標準時間) 這樣的格式,而我們需要讓它轉成 2024-10-22 才能被我們的 max 給接受,因此需要進行下一步:
.toISOString():把日期格式轉成 YYYY-MM-DDTHH:mm:ss.sssZ
toISOString() 是 Date 物件的一個內建方法,他會返回一個 ISO 8601(國際標準化組織的日期和時間的表示方法) 格式的字符串,但要注意一個問題是它返回的值會是 UTC 時區的值,也就是以台灣 UTC+8 來說,它的 HH 會比我們使用當下要少 8 小時,假設我們使用的時間在早上八點以前,那麼得到的 max 日期就不會是當天,而是前一天。
如果想避免這個情況發生,也可以用函式手動將 Date 物件進行格式化,格式化的函式放到最後再來說明。
.split(“T”)[0]:提取 YYYY-MM-DD
前面已經透過 .toISOString() 把 Date 物件轉成 YYYY-MM-DDTHH:mm:ss.sssZ 格式了,但我們需要的部份其實只有 YYYY-MM-DD 這部份而已,因此用字串內建的方法 split(),以T這個字為切割點,把整個字串切成了陣列:[ ‘YYYY-MM-DD‘, ‘HH:mm:ss.sssZ‘ ],而其中第 0 項的 ‘YYYY-MM-DD’ 就是我們需要的部份。
其實用另一個字串的內建方法 slice() 也能達到一樣的效果,因為 YYYY-MM-DDTHH:mm:ss.sssZ 的格式是固定的,我們需要的 YYYY-MM-DD 區塊必定是字串的前十個字,因此用 .slice(0,10) 也可以獲得我們需要的部份,寫法會像這樣:
function setDateRange() {
maxDate.value = today.toISOString().slice(0,10);
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().slice(0,10);
}
如何手動格式化 Date 物件
為了避免時差導致日期錯誤,我們可以設計一個函式來將 Date 物件手動格式化成 YYYY-MM-DD 的格式,具體寫法如下:
const today = new Date()
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
formatDate(today);
這個函式的思路很簡單,就是分別把日期物件的年、月、日提取出來之後,再重新組合成我們要的格式。其中值得注意的部份有兩點:
套用到案例中的話,就會是這樣:
<template>
<input type="date" :max="maxDate" :min="minDate">
</template>
<script setup>
import { ref, onMounted } from 'vue';
const today = new Date;
const maxDate = ref('');
const minDate = ref('');
function setDateRange() {
maxDate.value = formatDate(today);
today.setFullYear(today.getFullYear() - 1);
minDate.value = formatDate(today);
}
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
onMounted(() => {
setDateRange();
})
</script>
JavaScript 之旅 (5):String method - padStart & padEnd
AI 工具 - Microsoft Copilot