<input type="month">

Елементи <input> типу month (місяць) створює поля введення, що дають користувачеві змогу задати місяць і рік, дозволяючи легко їх ввести. Значенням є рядок, чиє значення має формат YYYY-MM, де YYYY – це чотирицифровий рік, а MM – номер місяця.

Спробуйте його в дії

Вигляд цього контрольного елемента, загалом, відрізняється від браузера до браузера; наразі підтримка не повна: лише Chrome, Opera й Edge на настільних комп'ютерах, а також сучасні версії більшості мобільних браузерів мають робочі реалізації. У браузерах, що не підтримують полів month, цей елемент доладно відступає до простого <input type="text">, а іще може бути автоматична валідація введеного тексту, аби пересвідчитись, що він має очікуваний формат.

Для тих, хто використовує браузер, що не підтримує month, знімок екрана нижче демонструє, який вигляд це має в Chrome та Opera. Клацання стрілки донизу з правого боку викликає інтерфейс вибору дати, що дає змогу обрати місяць і рік.

Поле місяця в браузері Chrome

Контрольний елемент month у Microsoft Edge має такий вигляд:

Поле місяця в браузері Edge

Значення

Рядок, що представляє значення місяця та року, введене в поле, у формі YYYY-MM (чотири або більше цифр року, потім дефіс (-), після якого – дві цифри місяця). Такий формат рядка місяця, вживаний цим типом поля, описаний в Рядках місяця.

Задання усталеного значення

Усталене значення контрольного елемента можна задати в атрибуті value, отак:

<label for="bday-month">В якому місяці ви народилися?</label>
<input id="bday-month" type="month" name="bday-month" value="2001-06" />

Одна річ, котру слід зауважити, полягає в тому, що показаний формат дати відрізняється від фактичного value; більшість користувацьких агентів демонструє рік та місяць в формі, згідній з локаллю, заданою як локаль операційної системи користувача, натомість value завжди має формат yyyy-MM.

Коли значення вище подається на сервер, наприклад, то воно має вигляд bday-month=1978-06.

Задання значення засобами JavaScript

Також отримувати та задавати значення дати в JavaScript можна за допомогою властивості HTMLInputElement.value, наприклад, так:

<label for="bday-month">В якому місяці ви народилися?</label>
<input id="bday-month" type="month" name="bday-month" />
const monthControl = document.querySelector('input[type="month"]');
monthControl.value = "2001-06";

Додаткові атрибути

На додачу до атрибутів, спільних для елементів <input>, поля місяця приймають наступні.

list

Значення атрибута list – це id елемента <datalist>, розташованого в тому самому документі. <datalist> надає список наперед визначених значень, що пропонуються користувачеві як значення для цього поля. Будь-які значення в списку, несумісні з type, не виводяться як запропоновані варіанти. Надані значення пропонуються, а не вимагаються: користувач може як обрати з такого наперед визначеного списку, так і ввести інше значення.

max

Найпізніші рік з місяцем, що приймаються, у форматі рядка, описаному в розділі Значення вище. Якщо введене в елемент value перевищує це значення, то елемент не проходить валідацію обмежень. Якщо значення атрибута max не є дійсним рядком у форматі yyyy-MM, то елемент не має максимального значення.

Це значення повинно задавати пару року та місяця, що є пізнішою або рівною парі, заданій в атрибуті min.

min

Найраніші рік з місяцем, в тому самому форматі yyyy-MM, описаному вище. Якщо value елемента менше за це значення, то такий елемент не проходить валідацію обмежень. Якщо значення, задане для min, не є дійсним рядком року та місяця, то поле не має мінімального значення.

Це значення повинно бути парою року та місяця, що є ранішою або рівною парі, заданій в атрибуті max.

readonly

Булів атрибут, котрий, коли присутній, означає, що таке поле не може бути змінено користувачем. Проте його value все одно може бути змінено з коду JavaScript, що безпосередньо задає значення властивості HTMLInputElement.value.

[!NOTE] У зв'язку з тим, що поле, доступне лише для прочитання, не може мати значення, required ніяк не діє на поля, на котрих також задано атрибут readonly.

step

Атрибут step – це число, що задає гранулярність, котрої повинно дотримуватись значення, або особливе значення any, описане нижче. Лише значення, що відповідають основі крокування (min, якщо цей атрибут задано, а інакше – value, або відповідне усталене значення, якщо ці атрибути не задані), вважаються дійсними.

Рядкове значення any означає, що крокування не накладається, і приймається будь-яке значення (з урахуванням інших обмежень, як то min і max).

[!NOTE] Коли дані, введені користувачем, не відповідають налаштуванням крокування, то користувацький агент може заокруглити до найближчого дійсного значення, віддаючи перевагу числам в додатному напрямку, коли є два рівновіддалені варіанти.

Для полів month значення step задаються в місяцях, з масштабним фактором 1 (оскільки числове значення за лаштунками – також в місяцях). Усталене значення step – 1 місяць.

Використання полів місяця

Поля, що стосуються дат (включно з month), на перший погляд, здаються зручними; вони обіцяють легкий користувацький інтерфейс для вибору дат, а також нормалізують формат даних, що надсилається на сервер, незалежно від локалі користувача. Проте з <input type="month"> є проблеми, тому що серед основних браузерів чимало його ще не підтримує.

Зрештою, погляньмо на просте та складніше застосування <input type="month">, а тоді ми дамо пораду щодо розв'язання проблеми з підтримкою браузерами – в розділі Робота з підтримкою браузерами).

Базове використання month

Найпростіше використання <input type="month"> включає базову комбінацію елементів <input> і <label>, як це видно нижче:

<form>
  <label for="bday-month">В якому місяці ви народилися?</label>
  <input id="bday-month" type="month" name="bday-month" />
</form>

Задання максимальної та мінімальної дат

Атрибути min і max можна використовувати для обмеження діапазону дат, з котрого може обирати користувач. В наступному прикладі задано мінімальний місяць 1900-01 і максимальний – 2013-12:

<form>
  <label for="bday-month">В якому місяці ви народилися?</label>
  <input
    id="bday-month"
    type="month"
    name="bday-month"
    min="1900-01"
    max="2013-12" />
</form>

Результатом тут є наступне:

  • Можуть бути вибрані місяці лише між січнем 1900 і груднем 2013; до місяців поза цим діапазоном не можна докрутити в контрольному елементі.
  • Залежно від браузера, що використовується, може виявитись, що місяць поза заданим діапазоном не можна вибрати в інтерфейсі вибору місяця (наприклад, в Edge), або що вони є недійсними (дивіться розділ Валідації), але їх все одно можна вибрати (наприклад, у Chrome).

Контроль розміру поля

<input type="month"> не підтримує атрибутів розміру форм, наприклад, size. Для задання його розміру доведеться вдатися до CSS.

Валідація

Усталено <input type="month"> не застосовує до введених значень жодної валідації. Загалом реалізації користувацького інтерфейсу не дозволяють вводити нічого, що не є датою, що корисно, але все одно можна подати форму, в якій поле month буде порожнім, або ввести недійсну дату (наприклад, 32 квітня).

Щоб цього уникнути, можна використати min і max – для обмеження доступних даних (дивіться Задання максимальної та мінімальної дат), і на додачу – застосувати атрибут required, аби зробити введення дати обов'язковим. Як наслідок, браузери, що це підтримують, покажуть помилку, якщо спробувати подати дату, що лежить поза заданими межами, або порожнє поле.

Погляньмо на приклад; тут задано мінімальну та максимальну дати, а також поле зроблено обов'язковим:

<form>
  <div>
    <label for="month">
      В якому місяці ви хотіли б приїхати (від червня до вересня)?
    </label>
    <input
      id="month"
      type="month"
      name="month"
      min="2022-06"
      max="2022-09"
      required />
    <span class="validity"></span>
  </div>
  <div>
    <input type="submit" value="Подати форму" />
  </div>
</form>

Якщо спробувати подати форму, не задавши як місяць, так і рік (або з датою поза заданими межами), то браузер покаже помилку. Спробуйте погратися з цим прикладом:

Ось знімок екрана для тих, хто не використовує браузер, що це підтримує:

Повідомлення про обов'язковість місяця в браузері Chrome

Ось CSS, застосований до прикладу вище. Тут ми користуємося властивостями CSS :valid і :invalid, аби оформити поле на основі того, чи є поточне його значення дійсним. Довелося поставити піктограми в <span> поруч з полем, а не в саме поле, тому що в Chrome згенерований вміст розташовується всередині контрольного елемента форми, і його не можна ефективно оформити чи продемонструвати.

div {
  margin-bottom: 10px;
  position: relative;
}

input[type="number"] {
  width: 100px;
}

input + span {
  padding-right: 30px;
}

input:invalid + span::after {
  position: absolute;
  content: "✖";
  padding-left: 5px;
}

input:valid + span::after {
  position: absolute;
  content: "✓";
  padding-left: 5px;
}

[!WARNING] Валідація форм HTML не є заміною сценаріїв, котрі пересвідчуються, що введені дані мають відповідний формат. Занадто легко будь-кому підлаштувати HTML, дозволивши собі обійти валідацію, чи взагалі її прибрати. Також хтось може обійти HTML узагалі та подати дані безпосередньо на сервер. Якщо ваш серверний код не може валідувати дані, котрі отримує, то може статись лихо, коли подадуть некоректно відформатовані дані (або завеликі дані, дані не того типу, і так далі).

Робота з підтримкою браузерами

Як згадувалося вище, головна проблема при використанні полів дат під час написання цієї статті – те, що серед головних браузерів чимало іще не мають реалізації їх усіх; лише Chrome, Opera та Edge підтримують їх на настільних комп'ютерах, а також більшість сучасних браузерів на мобільних. Наприклад, інтерфейс month на Chrome для Android має такий вигляд:

Інтерфейс вибору місяця на Chrome для Android

Браузери, що підтримки не мають, доладно відступають до текстового поля, але це породжує проблеми щодо сталості користувацького інтерфейсу (виведений контрольний елемент відрізняється), а також обробки даних.

Друга проблема – суттєвіша з двох. Як згадувалося вище, в полі month фактичне значення завжди нормалізується до формату yyyy-mm. З іншого боку, у своєму усталеному налаштуванні поле text не має уявлення, в якому форматі повинна бути дата, і це є проблемою через кількість різних способів, якими люди записують дати. Наприклад:

  • mmyyyy (072022)
  • mm/yyyy (07/2022)
  • mm-yyyy (07-2022)
  • yyyy-mm (2022-07)
  • Місяць yyyy (Липень 2022)
  • і так далі…

Один зі способів це обійти – додати полю month атрибут pattern. Попри те, що поле month його не використовує, якщо браузер відступить до обробки його як поля text, то такий патерн буде застосований. Наприклад, спробуйте переглянути наступне демо в браузері, що не підтримує поля month:

<form>
  <div>
    <label for="month">
      В якому місяці ви хотіли б приїхати (від червня до вересня)?
    </label>
    <input
      id="month"
      type="month"
      name="month"
      min="2022-06"
      max="2022-09"
      required
      pattern="[0-9]{4}-[0-9]{2}" />
    <span class="validity"></span>
  </div>
  <div>
    <input type="submit" value="Подати форму" />
  </div>
</form>

Якщо спробувати його подати, то буде видно, що браузер тепер показує повідомлення про помилку (та підсвічує недійсне поле), якщо введені дані не відповідають патернові nnnn-nn, де n – цифра від 0 до 9. Звісно, це не зупинить людей від уведення недійсних дат (наприклад, 0000-42), або некоректно відформатованих дат, що відповідають цьому патернові.

Іще є проблема з тим, що користувач не обов'язково знає, який формат серед багатьох – очікується. Треба зробити ще дещо.

Найкращий спосіб працювати з датами в формах у кросбраузерний спосіб (поки всі головні браузери не почнуть їх як слід підтримувати) полягає в тому, щоб дозволити користувачам вводити місяць і рік в окремих контрольних елементах (для цього популярні <select>; дивіться реалізацію нижче), або ж використовувати бібліотеки JavaScript, наприклад, втулку jQuery date picker.

Приклади

У цьому прикладі створюються два набори елементів інтерфейсу, кожний з яких розроблений, аби дати користувачеві змогу вибрати місяць і рік. Перший варіант – нативне поле month, а другий – пара елементів <select>, що дають змогу вибрати місяць і рік окремо, для сумісності з браузерами, що поки не підтримують <input type="month">.

HTML

Форма, що запитує місяць і рік, має такий вигляд:

<form>
  <div class="nativeDatePicker">
    <label for="month-visit">В якому місяці ви хотіли б нас відвідати?</label>
    <input type="month" id="month-visit" name="month-visit" />
    <span class="validity"></span>
  </div>
  <p class="fallbackLabel">В якому місяці ви хотіли б нас відвідати?</p>
  <div class="fallbackDatePicker">
    <div>
      <span>
        <label for="month">Місяць:</label>
        <select id="month" name="month">
          <option selected value="January">Січень</option>
          <option value="February">Лютий</option>
          <option value="March">Березень</option>
          <option value="April">Квітень</option>
          <option value="May">Травень</option>
          <option value="June">Червень</option>
          <option value="July">Липень</option>
          <option value="August">Серпень</option>
          <option value="September">Вересень</option>
          <option value="October">Жовтень</option>
          <option value="November">Листопад</option>
          <option value="December">Грудень</option>
        </select>
      </span>
      <span>
        <label for="year">Рік:</label>
        <select id="year" name="year"></select>
      </span>
    </div>
  </div>
</form>

Елемент <div> з ідентифікатором nativeDatePicker використовує поле типу month, щоб запитати місяць і рік, а <div> з ідентифікатором fallbackDatePicker замість цього використовує пару елементів <select>. Перший з них запитує місяць, а другий – рік.

В елемент <select> для вибору місяця жорстко закодовані назви місяців, адже вони не змінюються (якщо опустити локалізацію). Список доступних значень року – генерується динамічно, залежно від поточного року (шукайте детальні пояснення того, як ці функції працюють, в коментарях до коду нижче).

JavaScript

Код JavaScript, що обробляє вибір конкретного підходу й налаштовує список років для виводу в ненативному <select> з роками – нижче.

Можливо, найцікавіша частина прикладу – код зі з'ясування доступних можливостей. Аби з'ясувати, чи підтримує браузер <input type="month">, створюється новий елемент <input> зі спробою задати його type зі значенням month, а тоді негайно перевіряється, який тип фактично було задано. Браузери, що не підтримують тип month, повернуть text, оскільки це те, до чого відступає поле місяця, коли не підтримується. Якщо <input type="month"> не підтримується, то нативний інтерфейс вибору приховується, і виводиться запасний.

// Отримати елементи користувацького інтерфейсу
const nativePicker = document.querySelector(".nativeDatePicker");
const fallbackPicker = document.querySelector(".fallbackDatePicker");
const fallbackLabel = document.querySelector(".fallbackLabel");

const yearSelect = document.querySelector("#year");
const monthSelect = document.querySelector("#month");

// Спершу приховати запасний варіант
fallbackPicker.style.display = "none";
fallbackLabel.style.display = "none";

// Перевірити, чи відступає нове поле дати до текстового поля, чи ні
const test = document.createElement("input");

try {
  test.type = "month";
} catch (e) {
  console.log(e.description);
}

// Якщо це так, то запустити код всередині блоку if () {}
if (test.type === "text") {
  // Приховати нативний інтерфейс і показати запасний
  nativePicker.style.display = "none";
  fallbackPicker.style.display = "block";
  fallbackLabel.style.display = "block";

  // Заповнити роками динамічно
  // (місяці – завжди одні й ті ж, а тому – жорстко закодовані)
  populateYears();
}

function populateYears() {
  // Отримати поточний рік у вигляді числа
  const date = new Date();
  const year = date.getFullYear();

  // Зробити цей рік, а також 100 років перед ним, доступними в <select> року
  for (let i = 0; i <= 100; i++) {
    const option = document.createElement("option");
    option.textContent = year - i;
    yearSelect.appendChild(option);
  }
}

[!NOTE] Пам'ятайте, що частина років уміщає 53 тижні (дивіться Кількість тижнів у році)! Це доведеться враховувати при розробці промислових застосунків.

Технічний підсумок

Значення Рядок, що представляє місяць і рік, або порожній рядок.
Події change і input
Доступні спільні атрибути autocomplete, list, readonly, step
Атрибути IDL list, value, valueAsDate, valueAsNumber
Інтерфейс DOM

HTMLInputElement

Методи select(), stepDown(), stepUp().
Неявна роль ARIA немає відповідної ролі

Специфікації

Сумісність із браузерами

desktop mobile
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet
type="month"
Chrome Full support 20
Edge Full support 12
Firefox No support Ні
footnote
Internet Explorer No support Ні
Opera Full support 11
Safari No support Ні
footnote
WebView Android Full support Так
Chrome Android Full support Так
Firefox for Android Full support 18
Opera Android Full support Так
Safari on iOS Full support Так
Samsung Internet Full support Так

Дивіться також