week 周输入
<input type="week"> 是 HTML5 引入的周数选择控件,允许用户选择一个具体的年份和周数。该控件提交值格式为 YYYY-Www(例如 2025-W28),适用于按周规划的场景,如课程安排、项目排期、周报管理等。
前置知识
阅读本节前,建议先了解:month 月份输入
基础概念
什么是 week 输入
type="week" 输入框提供了一个年份和 ISO 周数的组合选择界面。主要特点:
- 年周一体化:年份和周数在同一个控件中选择
- ISO 8601 标准:遵循 ISO 周数定义(周一为一周的第一天)
- 格式统一:提交值始终为
YYYY-Www格式 - 适合周期规划:自然适配按周安排的业务场景
ISO 8601 周数规则
ISO 8601 标准定义了周数的计算方式:
- 每周从周一开始,周日为第七天
- 每年的第一周是包含该年 1 月 4 日的那一周
- 一年最多 52 或 53 周
- 某年的第一周可能从前一年的 12 月就开始了
- 某年的最后一周可能延续到下一年的 1 月
浏览器支持
| 浏览器 | 支持情况 | 控件外观 |
|---|---|---|
| Chrome | ✅ 支持 | 年周选择器 |
| Firefox | ⚠️ 有限支持(93+) | 简单输入框 |
| Safari | ⚠️ 不支持 | 降级为文本输入 |
| Edge | ✅ 支持 | 年周选择器 |
| Opera | ✅ 支持 | 年周选择器 |
重要提示:
type="week"的浏览器兼容性是所有日期时间类型中最差的,Safari 目前不提供原生周选择器。在生产环境中使用时需要提供降级方案。
提交值格式
YYYY-Www其中 ww 是两位数的 ISO 周数(01-53)。
例如:2025-W28 表示 2025 年第 28 周。
语法
基本用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week 周输入示例</title>
</head>
<body>
<h2>选择周</h2>
<form>
<label for="work-week">工作周:</label>
<input type="week" id="work-week" name="work_week">
<button type="submit">提交</button>
</form>
</body>
</html>设置默认值
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week 默认值示例</title>
</head>
<body>
<h2>默认选中当前周</h2>
<form>
<label for="current-week">当前周:</label>
<input type="week" id="current-week" name="current_week" value="2025-W28">
<button type="submit">查询</button>
</form>
<script>
// 通过 JavaScript 获取当前 ISO 周数
function getISOWeek(date) {
const d = new Date(date.getTime());
d.setHours(0, 0, 0, 0);
// 周四在当前周中则该周属于当前年
d.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7));
const yearStart = new Date(d.getFullYear(), 0, 4);
const weekNumber = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
return { year: d.getFullYear(), week: weekNumber };
}
const now = new Date();
const isoWeek = getISOWeek(now);
const weekStr = `${isoWeek.year}-W${isoWeek.week.toString().padStart(2, '0')}`;
document.getElementById('current-week').value = weekStr;
</script>
</body>
</html>min 和 max 属性
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week 范围限制</title>
</head>
<body>
<h2>选择学期周</h2>
<form>
<label for="term-week">学期周次:</label>
<!-- 只能选择 2025 年的周 -->
<input
type="week"
id="term-week"
name="term_week"
min="2025-W01"
max="2025-W52"
required
>
<button type="submit">提交</button>
</form>
</body>
</html>| 属性 | 说明 | 格式 |
|---|---|---|
min | 最早可选周 | YYYY-Www |
max | 最晚可选周 | YYYY-Www |
详细说明
step 步进控制
step 属性控制周选择的粒度:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week step 示例</title>
</head>
<body>
<h2>周步进</h2>
<form>
<div>
<label for="every-week">每周可选(默认 step=1):</label>
<input type="week" id="every-week" name="every_week">
</div>
<div>
<label for="biweekly">每两周可选(step=2):</label>
<input type="week" id="biweekly" name="biweekly" step="2" min="2025-W01" value="2025-W01">
</div>
<div>
<label for="monthly-week">每四周(约每月):</label>
<input type="week" id="monthly-week" name="monthly_week" step="4" min="2025-W01" value="2025-W01">
</div>
</form>
</body>
</html>| step 值(周) | 含义 |
|---|---|
1(默认) | 每周可选 |
2 | 隔周可选 |
4 | 约每月可选 |
any | 不限制 |
valueAsDate 和 valueAsNumber
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week 值属性</title>
</head>
<body>
<h2>获取周值</h2>
<label for="my-week">选择周:</label>
<input type="week" id="my-week" name="my_week" value="2025-W28">
<div id="output"></div>
<script>
const weekInput = document.getElementById('my-week');
const output = document.getElementById('output');
weekInput.addEventListener('input', function () {
const str = this.value; // "2025-W28"
const date = this.valueAsDate; // 该周的周一
const num = this.valueAsNumber; // Unix 时间戳
if (date) {
// 计算该周的周日
const sunday = new Date(date);
sunday.setDate(sunday.getDate() + 6);
output.innerHTML = `
<p>value: ${str}</p>
<p>周一: ${date.toLocaleDateString('zh-CN')}</p>
<p>周日: ${sunday.toLocaleDateString('zh-CN')}</p>
<p>Unix 时间戳: ${num}</p>
`;
}
});
</script>
</body>
</html>ISO 周数计算工具函数
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>ISO 周计算</title>
</head>
<body>
<h2>ISO 周数计算工具</h2>
<label for="date-input">输入日期:</label>
<input type="date" id="date-input" name="date">
<p id="week-result"></p>
<script>
/**
* 获取日期的 ISO 周数
* @param {Date} date - 日期对象
* @returns {{ year: number, week: number }} ISO 年份和周数
*/
function getISOWeek(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return {
year: d.getUTCFullYear(),
week: Math.ceil((((d - yearStart) / 86400000) + 1) / 7)
};
}
/**
* 根据年份和周数获取该周的日期范围
* @param {number} year - 年份
* @param {number} week - 周数
* @returns {{ monday: Date, sunday: Date }} 该周的周一和周日
*/
function getWeekDates(year, week) {
// 找到该年的 1 月 4 日(ISO 第一周必须包含 1 月 4 日)
const jan4 = new Date(year, 0, 4);
const dayOfWeek = jan4.getDay() || 7; // 周日 = 7
const monday = new Date(year, 0, 4 - (dayOfWeek - 1) + (week - 1) * 7);
const sunday = new Date(monday);
sunday.setDate(sunday.getDate() + 6);
return { monday, sunday };
}
// 使用示例
document.getElementById('date-input').addEventListener('change', function () {
if (this.value) {
const date = new Date(this.value + 'T00:00:00');
const iso = getISOWeek(date);
const weekStr = `${iso.year}-W${iso.week.toString().padStart(2, '0')}`;
const dates = getWeekDates(iso.year, iso.week);
document.getElementById('week-result').textContent =
`${this.value} 属于 ${weekStr}` +
`(${dates.monday.toLocaleDateString('zh-CN')} ~ ${dates.sunday.toLocaleDateString('zh-CN')})`;
}
});
</script>
</body>
</html>实战示例
项目周报选择器
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>项目周报</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
max-width: 700px;
margin: 40px auto;
padding: 0 20px;
}
.controls { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
input[type="week"] {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
input[type="week"]:focus { border-color: #e74c3c; outline: none; }
button {
padding: 8px 16px;
background: #e74c3c;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
}
.report-card {
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 20px;
margin-top: 16px;
display: none;
}
.report-card h3 { color: #e74c3c; margin-top: 0; }
textarea {
width: 100%;
min-height: 100px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
</style>
</head>
<body>
<h2>项目周报</h2>
<div class="controls">
<label for="report-week">选择周次:</label>
<input type="week" id="report-week" name="report_week">
<button type="button" id="load-btn">加载周报</button>
</div>
<div class="report-card" id="report-card">
<h3 id="week-title"></h3>
<div>
<label for="progress">本周进展:</label>
<textarea id="progress" placeholder="请填写本周工作进展..."></textarea>
</div>
<div>
<label for="plan">下周计划:</label>
<textarea id="plan" placeholder="请填写下周工作计划..."></textarea>
</div>
<button type="button" id="save-btn">保存周报</button>
</div>
<script>
const weekInput = document.getElementById('report-week');
const reportCard = document.getElementById('report-card');
// 设置当前 ISO 周为默认值
function getISOWeek(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return { year: d.getUTCFullYear(), week: Math.ceil((((d - yearStart) / 86400000) + 1) / 7) };
}
const iso = getISOWeek(new Date());
weekInput.value = `${iso.year}-W${iso.week.toString().padStart(2, '0')}`;
// 加载周报
document.getElementById('load-btn').addEventListener('click', function () {
const [year, weekPart] = weekInput.value.split('-');
const week = parseInt(weekPart.replace('W', ''));
// 计算该周的日期范围
const jan4 = new Date(parseInt(year), 0, 4);
const dayOfWeek = jan4.getDay() || 7;
const monday = new Date(parseInt(year), 0, 4 - (dayOfWeek - 1) + (week - 1) * 7);
const sunday = new Date(monday);
sunday.setDate(sunday.getDate() + 6);
reportCard.style.display = 'block';
document.getElementById('week-title').textContent =
`第 ${week} 周(${monday.toLocaleDateString('zh-CN')} ~ ${sunday.toLocaleDateString('zh-CN')})`;
});
</script>
</body>
</html>注意事项
浏览器兼容性有限
type="week" 在 Safari 中不被支持,会降级为普通文本输入框。建议提供降级方案:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>week 降级方案</title>
</head>
<body>
<h2>周选择器(含降级)</h2>
<form>
<label for="select-week">选择周:</label>
<input type="week" id="select-week" name="select_week">
<p id="fallback-hint" style="color: #999; font-size: 14px; display: none;">
请输入格式:YYYY-Www(例如:2025-W28)
</p>
<!-- 降级方案:年份+周数分离选择 -->
<div id="fallback-selects" style="display: none; margin-top: 12px;">
<label for="fallback-year">年份:</label>
<select id="fallback-year">
<option>2024</option>
<option selected>2025</option>
<option>2026</option>
</select>
<label for="fallback-week">周数:</label>
<select id="fallback-week">
<!-- 动态生成 1-53 -->
</select>
</div>
</form>
<script>
// 检测浏览器是否支持 type="week"
const testInput = document.createElement('input');
testInput.setAttribute('type', 'week');
const isWeekSupported = testInput.type === 'week';
if (!isWeekSupported) {
document.getElementById('fallback-hint').style.display = 'block';
document.getElementById('fallback-selects').style.display = 'block';
// 生成周数选项
const weekSelect = document.getElementById('fallback-week');
for (let i = 1; i <= 53; i++) {
const opt = document.createElement('option');
opt.value = i.toString().padStart(2, '0');
opt.textContent = `第 ${i} 周`;
weekSelect.appendChild(opt);
}
}
</script>
</body>
</html>ISO 周与自然月的错位
ISO 周数可能与自然月份不完全对齐。例如,2025 年 1 月 1 日(周三)属于 2025-W01,但 2024 年 12 月 30 日(周一)也属于 2025-W01。
step 与 min 的配合
使用 step 限制时,必须配合 min 属性使用才能确保周数对齐正确:
html
<!-- 必须设置 min,否则隔周选择可能从任意周开始 -->
<input type="week" name="biweekly" step="2" min="2025-W01" value="2025-W01">最佳实践
- 评估兼容性需求:如果目标用户包含 Safari 用户,建议提供降级方案
- 提供日期范围提示:由于周数不直观,应在旁边显示对应的日期范围
- 使用 ISO 周计算函数:确保前后端使用相同的 ISO 周数计算规则
- 结合可视化日历:考虑在周选择器下方显示当周的日历,增强可读性
- 合理设置 step:对于隔周报告等场景,使用
step="2"限制可选周数 - 格式验证:在服务端验证提交的周数格式和有效性
下一节
继续学习:range 滑块输入