1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
|
---
title: <input type="date">
slug: Web/HTML/Element/Input/date
translation_of: Web/HTML/Element/input/date
---
<p>{{HTMLRef}}</p>
<p>日期类{{htmlelement("input")}}元素会创建一个让用户输入一个日期的输入区域,可以使用自动验证内容的文本框,也可以使用特殊的日期选择器界面。结果值包括年份,月份和日期,但不包括时间。<a href="/en-US/docs/Web/HTML/Element/input/time">time</a> 和 <a href="/en-US/docs/Web/HTML/Element/input/datetime-local">datetime-local</a>输入类支持 time 和 date/time 输入。</p>
<p>通常来说控件的 UI 界面因浏览器的不同而有变化,到目前为止此控件还不被所有浏览器支持,具体细节请参阅<a href="#浏览器兼容性">浏览器兼容性</a>。在不支持的浏览器当中,控件因此会被优雅的降级为普通的<a href="/en-US/docs/Web/HTML/Element/input/text"><input type="text"></a>输入框。</p>
<div id="Basic_example">
<pre class="brush: html"><input id="date" type="date"></pre>
<p>{{ EmbedLiveSample('Basic_example', 600, 40) }}</p>
</div>
<p>其中,Chrome/Opera 浏览器为用户选择日期提供了一个普通的操作界面,看起来是下面的样子:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/14909/date-picker-chrome.png" style="display: block; height: 220px; margin: 0px auto; width: 275px;"></p>
<p>Edge 的日期控件看起来是这样子的:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/14911/date-picker-edge.png" style="display: block; margin: 0 auto;"></p>
<p>Firefox 的日期控件看起来是这样子的:</p>
<p><img alt="Datepicker UI in firefox" src="https://mdn.mozillademos.org/files/15644/firefox_datepicker.png" style="display: block; margin: 0 auto;"></p>
<table class="properties">
<tbody>
<tr>
<td><strong><a href="#value">Value</a></strong></td>
<td>一个代表按照 YYYY-MM-DD 格式化过的日期 {{domxref("DOMString")}},或者是为空</td>
</tr>
<tr>
<td><strong>Events</strong></td>
<td>{{event("change")}} 事件和 {{event("input")}}事件</td>
</tr>
<tr>
<td><strong>Supported Common Attributes</strong></td>
<td>{{htmlattrxref("autocomplete", "input")}}, {{htmlattrxref("list", "input")}}, {{htmlattrxref("readonly", "input")}}, 和 {{htmlattrxref("step", "input")}}</td>
</tr>
<tr>
<td><strong>IDL attributes</strong></td>
<td><code>list</code>, <code>value</code>, <code>valueAsDate</code>, <code>valueAsNumber</code>.</td>
</tr>
<tr>
<td><strong>Methods</strong></td>
<td>{{domxref("HTMLInputElement.select", "select()")}}, {{domxref("HTMLInputElement.stepDown", "stepDown()")}}, {{domxref("HTMLInputElement.stepUp", "stepUp()")}}</td>
</tr>
</tbody>
</table>
<h2 id="Value">Value</h2>
<p>一个 {{domxref("DOMString")}} 代表着输入到输入框的日期值。您可以通过在 {{htmlattrxref("value", "input")}} 属性中包含日期来为输入设置默认值,如下所示:</p>
<pre class="brush: html"><input id="date" type="date" value="2017-06-01"></pre>
<p>{{ EmbedLiveSample('Value', 600, 40) }}</p>
<p>有一点需要注意的是,在格式方面显示的日期与实际的不一样 — 显示的日期格式取决于用户浏览器的区域设定,而日期值的格式始终为 <code>yyyy-mm-dd。</code></p>
<p>当然你也可以在 JavaScript 中通过 input 元素的 {{domxref("HTMLInputElement.value", "value")}} 属性获取和设置日期值,例如:</p>
<pre class="brush: js">var dateControl = document.querySelector('input[type="date"]');
dateControl.value = '2017-06-01';</pre>
<p>这行代码查找类型为 date 的第一个 input 元素,并且将其值设置为 2017-06-01(2017 年 6 月 1 日)</p>
<h2 id="使用日期输入控件">使用日期输入控件</h2>
<p>日期控件,一开始听起来可能觉得很方便。它们不仅提供了一个简单的日期选择 UI 界面,还规范了发往后台的数据格式,无论用户在什么区域。但是,由于浏览器支持的限制,<input type="date"> 仍然存在一些问题。</p>
<p>我们将探寻一些关于 <input type="date"> 基础的和更复杂的的用法,然后就以后减轻浏览器支持问题提供建议(请参阅<a href="#处理浏览器支持问题">处理浏览器支持问题</a>)。</p>
<h3 id="日期控件基本用法">日期控件基本用法</h3>
<p>一个基础的 <input> 和 {{htmlelement("label")}} 元素组合是 <input type="date"> 最简单的使用方法,如下所示:</p>
<pre class="brush: html"><form>
<div>
<label for="bday">Enter your birthday:</label>
<input type="date" id="bday" name="bday">
</div>
</form></pre>
<p>{{ EmbedLiveSample('Basic_uses_of_date', 600, 40) }}</p>
<h3 id="设置日期最大和最小值">设置日期最大和最小值</h3>
<p>你可以通过 {{htmlattrxref("min", "input")}} 和 {{htmlattrxref("max", "input")}} 属性去限制用户的可选日期范围。在随后的例子中,我们将设定日期最小值为 <code>2017-04-01</code> 最大值为 <code>2017-04-30 </code>。</p>
<pre class="brush: html"><form>
<div>
<label for="party">Choose your preferred party date:</label>
<input type="date" id="party" name="party" min="2017-04-01" max="2017-04-30">
</div>
</form></pre>
<p>{{ EmbedLiveSample('Setting_maximum_and_minimum_dates', 600, 40) }}</p>
<p>在结果中我们可以看到,只有 2017 年 4 月份的日期可选 — 输入框中可以编辑的部分只有 “日” 这部分,并且超出 4 月份以外的日期不能通过日期控件的选择小部件选择。</p>
<div class="note">
<p><strong>注意</strong>: 您应该可以使用 step 属性来改变每次最佳日期时步进(增加值)的天数(例如:或许你只希望使周六可以选)。但是,在编写本文的任何实现中,这似乎并不奏效。</p>
</div>
<h3 id="控制输入框大小">控制输入框大小</h3>
<p><code><input type="date"></code> 不支持表单属性 {{htmlattrxref("size", "input")}}. 对于大小需求,你必须祈求于 <a href="/en-US/docs/Web/CSS">CSS</a> 的帮助。</p>
<h2 id="验证">验证</h2>
<p>默认情况下,<code><input type="date"></code> 对输入的值不会做任何校验。 UI 实现通常不会让你输入任何不适日期的东西 — 这一点很有帮助 — 但是你任然可以留空或者 (在被优雅降级为 <code>text</code> 类型的输入框) 输入一个不合法的值 (例如: 4 月 32 号)。</p>
<p>如果你使用 {{htmlattrxref("min", "input")}} 和 {{htmlattrxref("min", "input")}} 属性去限制可用日期 (参见 <a href="#设置日期最大和最小值">设置日期最大和最小值</a>),对于支持的浏览器来说如果你尝试提交一个超出给定范围的日期,那么它将抛出一个错误。然而, 你必须检查这些结果以确保他们在这些日期范围内, 因为只有在用户设备上完全支持日期选择器的情况下,才能执行这些操作。</p>
<p>另外, 您可以使用 {{htmlattrxref("required", "input")}} 属性强制填写日期, 如果你尝试提交一个未填写日期的域那么将会抛出错误。 至少在大多数浏览器是可以工作的。</p>
<p>让我们看一个例子 — 我们设置了日期的最大和最小值, 并且设定为必填:</p>
<pre class="brush: html"><form>
<div>
<label for="party">Choose your preferred party date (required, April 1st to 20th):</label>
<input type="date" id="party" name="party" min="2017-04-01" max="2017-04-20" required>
<span class="validity"></span>
</div>
<div>
<input type="submit">
</div>
</form></pre>
<p>如果你尝试提交一个不完整日期的表单 (或者超出日期选择设定范围), 浏览器将会出现一个错误。 尝试一下这个例子:</p>
<p>{{ EmbedLiveSample('Validation', 600, 100) }}</p>
<p>这个截图是为那些浏览器不支持的人准备的:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/14913/date-picker-chrome-error-message.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
<p>这是上面例子使用的 css。我们用 {{cssxref(":valid")}} and {{cssxref(":invalid")}} 属性去命名,以区别当前值的有效性 。我们必须把图标放在 input 旁边的 {{htmlelement("span")}}里面,而并不是它本身, 因为在 Chrome 中被放置在表单中生成的内容不能有效的样式化或者显示。</p>
<pre class="brush: css">div {
margin-bottom: 10px;
display: flex;
align-items: center;
}
label {
display: inline-block;
width: 300px;
}
input:invalid+span:after {
content: '✖';
padding-left: 5px;
}
input:valid+span:after {
content: '✓';
padding-left: 5px;
}</pre>
<div class="warning">
<p><strong>重要</strong>: HTML 表单验证并不能替代脚本去确保输入值是有效的格式。一些人很容易调整 HTML 绕过验证,或者完全移除验证.。当然一些人也可能很容易的绕过你的验证,直接把数据提交到你的服务器。如果你的服务器无法验证它接收到的数据,当提交了不正确的数据之后可能会导致灾难性的后果 (或者数据量太大,错误的类型等等).</p>
</div>
<h2 id="处理浏览器支持问题">处理浏览器支持问题</h2>
<p>正如刚才提到的,在编写本文时使用日期输入的主要问题就是 <a href="#浏览器兼容性">浏览器兼容性</a>。举一个例子, 在安卓系统的 Firefox 中选择器是这样子的:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/14915/date-picker-fxa.png" style="display: block; margin: 0 auto;"></p>
<p>在不支持的浏览器上会被降级为文本输入框, 但这同时带来了用户界面不统一(呈现的控件不同)和数据处理方面的问题。</p>
<p>第二个问题更为严重;正如我们早些时候提到的,对于日期输入框, 实际值总是会被格式化微 <code>yyyy-mm-dd</code>。另一方面对于文本输入框,默认情况下浏览器并不知道日期格式应该怎么样被格式化, 而且人们书写日期格式的方式有很多,例如:</p>
<ul>
<li><code>ddmmyyyy</code></li>
<li><code>dd/mm/yyyy</code></li>
<li><code>mm/dd/yyyy</code></li>
<li><code>dd-mm-yyyy</code></li>
<li><code>mm-dd-yyyy</code></li>
<li><code>Month dd yyyy</code></li>
</ul>
<p> 解决这些问题的方法之一就是放置一个 {{htmlattrxref("pattern", "input")}} 属性在日期控件上 。即使日期输入不使用它,文本输入将会用到。例如, 请尝试在不支持的浏览器上看下面的例子:</p>
<pre class="brush: html"><form>
<div>
<label for="bday">Enter your birthday:</label>
<input type="date" id="bday" name="bday" required pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
<span class="validity"></span>
</div>
<div>
<input type="submit">
</div>
</form></pre>
<p>{{ EmbedLiveSample('Handling_browser_support', 600, 100) }}</p>
<p>如果你尝试提交,如果你的输入不符合正则表达式 <code>nnn-nn-nn</code>(<code>n</code> 是 0 到 9 的数字), 你将会看到浏览器显示一个错误 (并且高亮显示输入无效) 。 当然,这并不能阻止人们输入无效的日期或者格式不正确的日期,例如<code>yyyy-dd-mm</code> (而我们想要的 <code>yyyy-mm-dd</code>)。因此我们仍然有一个问题。</p>
<div class="hidden">
<pre class="brush: css">div {
margin-bottom: 10px;
}
input:invalid + span {
position: relative;
}
input:invalid + span:after {
content: '✖';
position: absolute;
right: -18px;
}
input:valid + span {
position: relative;
}
input:valid + span:after {
content: '✓';
position: absolute;
right: -18px;
}</pre>
</div>
<p>目前以跨浏览器方式处理表单中日期的最佳方式是让用户在单独的控件中输入 日 , 月 和 年 ({{htmlelement("select")}} 元素正越来越受欢迎; 请看下面的实现), 或者使用 JavaScript 库, 例如 <a href="https://jqueryui.com/datepicker/">jQuery date picker</a>.</p>
<h2 id="例子">例子</h2>
<p>在这个例子中,我们创建了两组用于选择日期的 UI 元素: 一个本地 <code><input type="date"></code> 选择器 和 一组三个 {{htmlelement("select")}} 元素用于选择不支持本地输入的旧浏览器中的日期。</p>
<p>{{ EmbedLiveSample('例子', 600, 100) }}</p>
<h3 id="HTML">HTML</h3>
<p>HTML 看起来像这样:</p>
<pre class="brush: html"><form>
<div class="nativeDatePicker">
<label for="bday">Enter your birthday:</label>
<input type="date" id="bday" name="bday">
<span class="validity"></span>
</div>
<p class="fallbackLabel">Enter your birthday:</p>
<div class="fallbackDatePicker">
<span>
<label for="day">Day:</label>
<select id="day" name="day">
</select>
</span>
<span>
<label for="month">Month:</label>
<select id="month" name="month">
<option selected>January</option>
<option>February</option>
<option>March</option>
<option>April</option>
<option>May</option>
<option>June</option>
<option>July</option>
<option>August</option>
<option>September</option>
<option>October</option>
<option>November</option>
<option>December</option>
</select>
</span>
<span>
<label for="year">Year:</label>
<select id="year" name="year">
</select>
</span>
</div>
</form></pre>
<p>月份是写死的 (因为月份是固定的), 而日和年的值是根据当前选择的月份和年份(日的判定需要月份和年份)以及当年 动态生成的 (请参阅下面的代码注释,他们详细的阐释了这些功能是如何工作的。)</p>
<div class="hidden">
<pre class="brush: css">input:invalid+span:after {
content: '✖';
padding-left: 5px;
}
input:valid+span:after {
content: '✓';
padding-left: 5px;
}</pre>
</div>
<h3 id="JavaScript">JavaScript</h3>
<p>代码的另一部分也可能是最有意思的部分那就是特征检验代码 — 去检测浏览器是否支持 <code><input type="date"></code>, 我们创建一个新的 {{htmlelement("input")}} 元素, 设置它的 <code>type</code> 为 <code>date</code>,然后立刻检查它的类型 —不支持的浏览器将会返回 <code>text</code>, 因为 <code>date</code> 类型会被优雅的降级为<code>text</code>。如果 <code><input type="date"></code> 不被浏览器支持, 我们隐藏本地选取器并用备用选取器 UI ({{htmlelement("select")}}) 替代。</p>
<pre class="brush: js">// define variables
var nativePicker = document.querySelector('.nativeDatePicker');
var fallbackPicker = document.querySelector('.fallbackDatePicker');
var fallbackLabel = document.querySelector('.fallbackLabel');
var yearSelect = document.querySelector('#year');
var monthSelect = document.querySelector('#month');
var daySelect = document.querySelector('#day');
// hide fallback initially
fallbackPicker.style.display = 'none';
fallbackLabel.style.display = 'none';
// test whether a new date input falls back to a text input or not
var test = document.createElement('input');
test.type = 'date';
// if it does, run the code inside the if() {} block
if(test.type === 'text') {
// hide the native picker and show the fallback
nativePicker.style.display = 'none';
fallbackPicker.style.display = 'block';
fallbackLabel.style.display = 'block';
// populate the days and years dynamically
// (the months are always the same, therefore hardcoded)
populateDays(monthSelect.value);
populateYears();
}
function populateDays(month) {
// delete the current set of <option> elements out of the
// day <select>, ready for the next set to be injected
while(daySelect.firstChild){
daySelect.removeChild(daySelect.firstChild);
}
// Create variable to hold new number of days to inject
var dayNum;
// 31 or 30 days?
if(month === 'January' || month === 'March' || month === 'May' || month === 'July' || month === 'August' || month === 'October' || month === 'December') {
dayNum = 31;
} else if(month === 'April' || month === 'June' || month === 'September' || month === 'November') {
dayNum = 30;
} else {
// If month is February, calculate whether it is a leap year or not
var year = yearSelect.value;
(year - 2016) % 4 === 0 ? dayNum = 29 : dayNum = 28;
}
// inject the right number of new <option> elements into the day <select>
for(i = 1; i <= dayNum; i++) {
var option = document.createElement('option');
option.textContent = i;
daySelect.appendChild(option);
}
// if previous day has already been set, set daySelect's value
// to that day, to avoid the day jumping back to 1 when you
// change the year
if(previousDay) {
daySelect.value = previousDay;
// If the previous day was set to a high number, say 31, and then
// you chose a month with less total days in it (e.g. February),
// this part of the code ensures that the highest day available
// is selected, rather than showing a blank daySelect
if(daySelect.value === "") {
daySelect.value = previousDay - 1;
}
if(daySelect.value === "") {
daySelect.value = previousDay - 2;
}
if(daySelect.value === "") {
daySelect.value = previousDay - 3;
}
}
}
function populateYears() {
// get this year as a number
var date = new Date();
var year = date.getFullYear();
// Make this year, and the 100 years before it available in the year <select>
for(var i = 0; i <= 100; i++) {
var option = document.createElement('option');
option.textContent = year-i;
yearSelect.appendChild(option);
}
}
// when the month or year <select> values are changed, rerun populateDays()
// in case the change affected the number of available days
yearSelect.onchange = function() {
populateDays(monthSelect.value);
}
monthSelect.onchange = function() {
populateDays(monthSelect.value);
}
//preserve day selection
var previousDay;
// update what day has been set to previously
// see end of populateDays() for usage
daySelect.onchange = function() {
previousDay = daySelect.value;
}</pre>
<div class="note">
<p><strong>注意</strong>: 请记住有些年份有 53 周 (请看 <a href="https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year">Weeks per year</a>)! 当你开发应用程序时需要考虑到这一点。</p>
</div>
<h2 id="规范">规范</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('HTML WHATWG', 'forms.html#date-state-(type=date)', '<input type="date">')}}</td>
<td>{{Spec2('HTML WHATWG')}}</td>
<td> </td>
</tr>
<tr>
<td>{{SpecName('HTML5 W3C', 'forms.html#date-state-(type=date)', '<input type="date">')}}</td>
<td>{{Spec2('HTML5 W3C')}}</td>
<td> </td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
{{Compat("html.elements.input.input-date")}}
<h2 id="参见">参见</h2>
<ul>
<li>通用的 {{HTMLElement("input")}} 元素 和 用于操作他的接口,{{domxref("HTMLInputElement")}}</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms/The_native_form_widgets#Date_and_time_picker">Date and Time picker tutorial</a></li>
</ul>
|