Швидкий пошук тут на CSS-Tricks показує, скільки різних способів підходу до календарів існує. Деякі показують, як CSS Grid може ефективно створювати макет. Деякі спроби внести фактичні дані в суміш, Дещо спиратися на каркас допомагати в державному управлінні.
Існує багато міркувань при створенні компонента календаря — набагато більше, ніж те, що розглядається в статтях, які я посилав. Якщо ви подумаєте про це, календарі сповнені нюансів, від обробки часових поясів і форматів дати до локалізації та навіть забезпечення переходу дат від одного місяця до наступного… і це ще до того, як ми навіть займемося доступністю та додатковими міркуваннями щодо макета залежно від місця розташування календаря. відображається та інше.
Багато розробників бояться Date()
об'єкт і дотримуйтеся старих бібліотек, таких як moment.js
. Але хоча є багато «неполадок», коли справа доходить до дат і форматування, JavaScript має багато цікавих API та іншого, щоб допомогти!
Я не хочу заново створювати колесо тут, але я покажу вам, як ми можемо отримати чудовий календар із ванільним JavaScript. Ми розглянемо доступність, використовуючи семантичну розмітку та зручний для читання з екрана <time>
-tags — а також інтернаціоналізація та форматування, використовуючи Intl.Locale
, Intl.DateTimeFormat
та Intl.NumberFormat
- API.
Іншими словами, ми створюємо календар… лише без додаткових залежностей, які зазвичай використовуються в подібних уроках, і з деякими нюансами, які ви зазвичай не бачите. І я сподіваюся, що в процесі цього ви по-новому оціните нові можливості JavaScript, у той же час отримаєте уявлення про речі, які спадають мені на думку, коли я збираю щось подібне.
По-перше, найменування
Як ми маємо назвати наш компонент календаря? На моїй рідній мові це буде називатися «елемент календаря», тож давайте використаємо це та скоротимо до «Каль-Ель» — також відомого як Ім'я Супермена на планеті Криптон.
Давайте створимо функцію, щоб все запрацювало:
function kalEl(settings = {}) { ... }
Цей метод візуалізує один місяць. Пізніше ми викличемо цей метод з [...Array(12).keys()]
відобразити цілий рік.
Вихідні дані та інтернаціоналізація
Однією зі звичайних речей звичайного онлайн-календаря є виділення поточної дати. Отже, давайте створимо для цього посилання:
const today = new Date();
Далі ми створимо «об’єкт конфігурації», який об’єднаємо з необов’язковим settings
об'єкт основного методу:
const config = Object.assign( { locale: (document.documentElement.getAttribute('lang') || 'en-US'), today: { day: today.getDate(), month: today.getMonth(), year: today.getFullYear() } }, settings
);
Перевіряємо, чи кореневий елемент (<html>
) містить а lang
-атрибут с місце дії інформація; інакше ми повернемося до використання en-US
. Це перший крок назустріч інтернаціоналізація календаря.
Нам також потрібно визначити, який місяць спочатку відображати під час візуалізації календаря. Ось чому ми розширили config
об'єкта з перв date
. Таким чином, якщо дата не вказана в settings
об'єкт, ми будемо використовувати today
натомість посилання:
const date = config.date ? new Date(config.date) : today;
Нам потрібно трохи більше інформації, щоб правильно відформатувати календар на основі мови. Наприклад, залежно від регіону ми можемо не знати, чи перший день тижня неділя чи понеділок. Якщо у нас є інформація, чудово! Але якщо ні, ми оновимо його за допомогою Intl.Locale
API. API має a weekInfo
об'єкт що повертає a firstDay
власність, яка дає нам саме те, що ми шукаємо, без будь-яких турбот. Ми також можемо дізнатися, які дні тижня призначені для weekend
:
if (!config.info) config.info = new Intl.Locale(config.locale).weekInfo || { firstDay: 7, weekend: [6, 7] };
Знову ми створюємо запасні варіанти. «Перший день» тижня для en-US
неділя, тому за умовчанням встановлено значення 7
. Це трохи заплутано, як getDay
метод у JavaScript повертає дні як [0-6]
, Де 0
неділя… не питайте мене чому. Тому вихідні – субота та неділя [6, 7]
.
Раніше ми мали Intl.Locale
API та його weekInfo
Методом було досить важко створити міжнародний календар без багатьох **об’єктів і масивів з інформацією про кожну локаль або регіон. Зараз це легко. Якщо ми проходимо en-GB
, метод повертає:
// en-GB
{ firstDay: 1, weekend: [6, 7], minimalDays: 4
}
У такій країні, як Бруней (ms-BN
), вихідні п'ятниця та неділя:
// ms-BN
{ firstDay: 7, weekend: [5, 7], minimalDays: 1
}
Вам може бути цікаво, що це minimalDays
власність є. Це все найменша кількість днів, необхідних для першого тижня місяця, щоб вважатися повним тижнем. У деяких регіонах це може бути лише один день. Для інших це може тривати цілих сім днів.
Далі ми створимо a render
метод в рамках нашого kalEl
-метод:
const render = (date, locale) => { ... }
Нам все ще потрібні додаткові дані для роботи, перш ніж ми щось візуалізуємо:
const month = date.getMonth();
const year = date.getFullYear();
const numOfDays = new Date(year, month + 1, 0).getDate();
const renderToday = (year === config.today.year) && (month === config.today.month);
Останній - a Boolean
що перевіряє чи today
існує в місяці, який ми збираємося відобразити.
Семантична розмітка
За мить ми заглибимося у рендеринг. Але спочатку я хочу переконатися, що налаштовані нами деталі мають семантичні теги HTML, пов’язані з ними. Налаштування цього відразу з коробки дає нам переваги доступності з самого початку.
Обгортка календаря
По-перше, ми маємо несемантичну оболонку: <kal-el>
. Це добре, тому що немає семантики <calendar>
тег або щось подібне. Якби ми не створювали власний елемент, <article>
може бути найбільш відповідним елементом, оскільки календар може стояти на окремій сторінці.
Назви місяців
Команда <time>
елемент буде важливим для нас, оскільки він допомагає перекладати дати у формат, який програми зчитування з екрану та пошукові системи можуть аналізувати точніше та послідовніше. Наприклад, ось як ми можемо передати «січень 2023» у нашій розмітці:
<time datetime="2023-01">January <i>2023</i></time>
Назви днів
Рядок над датами календаря, що містить назви днів тижня, може бути складним. Ідеально, якщо ми можемо написати повні назви для кожного дня — наприклад, неділя, понеділок, вівторок тощо — але це може зайняти багато місця. Отже, давайте скоротимо назви всередині an <ol>
де кожен день є a <li>
:
<ol> <li><abbr title="Sunday">Sun</abbr></li> <li><abbr title="Monday">Mon</abbr></li> <!-- etc. -->
</ol>
Ми можемо піти на хитрість із CSS, щоб отримати найкраще з обох світів. Наприклад, якщо ми трохи змінимо розмітку так:
<ol> <li> <abbr title="S">Sunday</abbr> </li>
</ol>
…ми отримуємо повні імена за замовчуванням. Потім ми можемо «сховати» повне ім’я, коли закінчиться місце, і відобразити title
натомість атрибут:
@media all and (max-width: 800px) { li abbr::after { content: attr(title); }
}
Але ми не йдемо цим шляхом, тому що Intl.DateTimeFormat
API може допомогти і тут. Ми поговоримо про це в наступному розділі, коли будемо розглядати візуалізацію.
Числа днів
Кожна дата в сітці календаря отримує номер. Кожне число є елементом списку (<li>
) у впорядкованому списку (<ol>
), і вбудований <time>
тег обертає фактичне число.
<li> <time datetime="2023-01-01">1</time>
</li>
І хоча я поки що не планую робити будь-який стиль, я знаю, що мені захочеться якось стилізувати цифри дат. Це можливо як є, але я також хочу мати можливість стилізувати числа будніх днів інакше, ніж числа вихідних, якщо мені це потрібно. Отже, я збираюся включити data-*
Атрибути спеціально для цього: data-weekend
та data-today
.
Номери тижнів
Рік має 52 тижні, іноді 53. Хоча це не дуже поширене явище, може бути добре відобразити число певного тижня в календарі для додаткового контексту. Мені подобається мати його зараз, навіть якщо я не перестану ним користуватися. Але ми повністю використаємо його в цьому підручнику.
Ми будемо використовувати а data-weeknumber
атрибут як гачок стилю та включити його в розмітку для кожної дати, яка є першою датою тижня.
<li data-day="7" data-weeknumber="1" data-weekend=""> <time datetime="2023-01-08">8</time>
</li>
надання
Давайте розмістимо календар на сторінці! Ми це вже знаємо <kal-el>
це ім'я нашого спеціального елемента. Перше, що нам потрібно налаштувати, це встановити firstDay
властивість на ньому, тому календар знає, неділя чи інший день є першим днем тижня.
<kal-el data-firstday="${ config.info.firstDay }">
Ми будемо використовувати літерали шаблонів щоб відобразити розмітку. Щоб відформатувати дати для міжнародної аудиторії, ми використаємо Intl.DateTimeFormat
API, знову використовуючи locale
ми вказали раніше.
Місяць і рік
Коли ми дзвонимо month
, ми можемо встановити, чи хочемо ми використовувати long
назва (наприклад, лютий) або short
ім'я (наприклад, лют.). Давайте використовувати long
ім'я, оскільки це заголовок над календарем:
<time datetime="${year}-${(pad(month))}"> ${new Intl.DateTimeFormat( locale, { month:'long'}).format(date)} <i>${year}</i>
</time>
Назви днів тижня
Для днів тижня, які відображаються над сіткою дат, нам потрібні обидва long
(наприклад, «неділя») і short
(скорочені, тобто «Сонечко») назви. Таким чином, ми можемо використовувати «коротку» назву, коли в календарі мало місця:
Intl.DateTimeFormat([locale], { weekday: 'long' })
Intl.DateTimeFormat([locale], { weekday: 'short' })
Давайте створимо невеликий допоміжний метод, який полегшить виклик кожного з них:
const weekdays = (firstDay, locale) => { const date = new Date(0); const arr = [...Array(7).keys()].map(i => { date.setDate(5 + i) return { long: new Intl.DateTimeFormat([locale], { weekday: 'long'}).format(date), short: new Intl.DateTimeFormat([locale], { weekday: 'short'}).format(date) } }) for (let i = 0; i < 8 - firstDay; i++) arr.splice(0, 0, arr.pop()); return arr;
}
Ось як ми викликаємо це в шаблоні:
<ol> ${weekdays(config.info.firstDay,locale).map(name => ` <li> <abbr title="${name.long}">${name.short}</abbr> </li>`).join('') }
</ol>
Числа днів
І, нарешті, дні, загорнуті в <ol>
Елемент:
${[...Array(numOfDays).keys()].map(i => { const cur = new Date(year, month, i + 1); let day = cur.getDay(); if (day === 0) day = 7; const today = renderToday && (config.today.day === i + 1) ? ' data-today':''; return ` <li data-day="${day}"${today}${i === 0 || day === config.info.firstDay ? ` data-weeknumber="${new Intl.NumberFormat(locale).format(getWeek(cur))}"`:''}${config.info.weekend.includes(day) ? ` data-weekend`:''}> <time datetime="${year}-${(pad(month))}-${pad(i)}" tabindex="0"> ${new Intl.NumberFormat(locale).format(i + 1)} </time> </li>`
}).join('')}
Давайте розберемо це:
- Ми створюємо «фіктивний» масив на основі змінної «кількість днів», який будемо використовувати для повторення.
- Ми створюємо
day
змінна для поточного дня в ітерації. - Виправляємо невідповідність між
Intl.Locale
API іgetDay()
. - Якщо
day
дорівнюєtoday
, додаємо аdata-*
атрибут. - Нарешті повертаємо
<li>
елемент у вигляді рядка з об’єднаними даними. tabindex="0"
робить елемент доступним для фокусування під час використання навігації з клавіатури після будь-яких позитивних значень tabindex (Примітка: ви повинні ніколи додавати позитивний tabindex-values)
До «пробивай» цифри в datetime
атрибут, ми використовуємо маленький допоміжний метод:
const pad = (val) => (val + 1).toString().padStart(2, '0');
Номер тижня
Знову ж таки, «номер тижня» — це місце тижня в 52-тижневому календарі. Для цього ми також використовуємо невеликий допоміжний метод:
function getWeek(cur) { const date = new Date(cur.getTime()); date.setHours(0, 0, 0, 0); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); const week = new Date(date.getFullYear(), 0, 4); return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7);
}
Я не писав цього getWeek
-метод. Це очищена версія цей скрипт.
І це все! Завдяки Intl.Locale
, Intl.DateTimeFormat
та Intl.NumberFormat
API, тепер ми можемо просто змінити lang
-атрибут <html>
елемент для зміни контексту календаря на основі поточного регіону:
Стилізація календаря
Ви можете пригадати, що всі дні лише один <ol>
з елементами списку. Щоб оформити їх у зручний для читання календар, ми занурюємось у чудовий світ CSS Grid. Фактично, ми можемо перепрофілювати ту саму сітку з стартовий шаблон календаря прямо тут, на CSS-Tricks, але трохи оновив за допомогою :is()
реляційний псевдо для оптимізації коду.
Зверніть увагу, що я визначаю змінні CSS, які можна налаштувати (і додаю їм префікс ---kalel-
щоб уникнути конфліктів).
kal-el :is(ol, ul) { display: grid; font-size: var(--kalel-fz, small); grid-row-gap: var(--kalel-row-gap, .33em); grid-template-columns: var(--kalel-gtc, repeat(7, 1fr)); list-style: none; margin: unset; padding: unset; position: relative;
}
Давайте намалюємо рамки навколо чисел дат, щоб допомогти їх візуально розділити:
kal-el :is(ol, ul) li { border-color: var(--kalel-li-bdc, hsl(0, 0%, 80%)); border-style: var(--kalel-li-bds, solid); border-width: var(--kalel-li-bdw, 0 0 1px 0); grid-column: var(--kalel-li-gc, initial); text-align: var(--kalel-li-tal, end); }
Сітка з семи стовпців добре працює, коли настає перший день місяця Також перший день тижня для вибраної мови). Але це радше виняток, ніж правило. У більшості випадків нам потрібно буде перенести перший день місяця на інший день тижня.
Запам'ятайте все зайве data-*
атрибути, які ми визначили під час написання нашої розмітки? Ми можемо підключитися до них, щоб оновити стовпець сітки (--kalel-li-gc
) перше число місяця ставиться на:
[data-firstday="1"] [data-day="3"]:first-child { --kalel-li-gc: 1 / 4;
}
У цьому випадку ми переходимо від першого стовпця сітки до четвертого стовпця сітки, що автоматично «виштовхне» наступний елемент (День 2) до п’ятого стовпця сітки тощо.
Давайте додамо трохи стилю «поточній» даті, щоб вона виділялася. Це лише мої стилі. Ви можете робити тут усе, що забажаєте.
[data-today] { --kalel-day-bdrs: 50%; --kalel-day-bg: hsl(0, 86%, 40%); --kalel-day-hover-bgc: hsl(0, 86%, 70%); --kalel-day-c: #fff;
}
Мені подобається ідея оформити номери дат у вихідні дні інакше, ніж у будні. Я збираюся використовувати червонуватий колір для їх стилізації. Зверніть увагу, що ми можемо досягти :not()
псевдоклас, щоб вибрати їх, залишаючи лише поточну дату:
[data-weekend]:not([data-today]) { --kalel-day-c: var(--kalel-weekend-c, hsl(0, 86%, 46%));
}
О, і давайте не забувати про номери тижнів, які йдуть перед першою датою кожного тижня. Ми використовували a data-weeknumber
у розмітці для цього, але числа насправді не відображатимуться, якщо ми не розкриємо їх за допомогою CSS, що ми можемо зробити на ::before
псевдоелемент:
[data-weeknumber]::before { display: var(--kalel-weeknumber-d, inline-block); content: attr(data-weeknumber); position: absolute; inset-inline-start: 0; /* additional styles */
}
Технічно ми готові! Ми можемо відобразити календарну сітку, яка показує дати поточного місяця, враховуючи локалізацію даних за мовними стандартами та гарантуючи, що календар використовує правильну семантику. І все, що ми використовували, це ванільний JavaScript і CSS!
Але давайте візьмемо це ще один крок...
Рендеринг цілого року
Можливо, вам потрібно відобразити цілий рік із датами! Отже, замість візуалізації поточного місяця ви можете відобразити всі сітки місяців поточного року.
Добре, приємна річ у підході, який ми використовуємо, полягає в тому, що ми можемо викликати render
метод скільки завгодно разів і просто змінюйте ціле число, яке ідентифікує місяць у кожному екземплярі. Давайте називати його 12 разів за поточним роком.
так просто, як зателефонувати render
-метод 12 разів і просто змініть ціле число для month
- i
:
[...Array(12).keys()].map(i => render( new Date(date.getFullYear(), i, date.getDate()), config.locale, date.getMonth() )
).join('')
Можливо, було б гарною ідеєю створити нову батьківську оболонку для візуалізованого року. Кожна календарна сітка є a <kal-el>
елемент. Давайте викличемо нову батьківську оболонку <jor-el>
, Де Джор-Ел — ім'я батька Кал-Ела.
<jor-el id="app" data-year="true"> <kal-el data-firstday="7"> <!-- etc. --> </kal-el> <!-- other months -->
</jor-el>
Ми можемо використовувати <jor-el>
щоб створити сітку для наших сіток. Так мета!
jor-el { background: var(--jorel-bg, none); display: var(--jorel-d, grid); gap: var(--jorel-gap, 2.5rem); grid-template-columns: var(--jorel-gtc, repeat(auto-fill, minmax(320px, 1fr))); padding: var(--jorel-p, 0);
}
Фінальна демонстрація
Бонус: Календар конфетті
Я прочитав чудову книгу під назвою Створення та розбивання сітки днями випадково натрапила на цей гарний «новорічний плакат»:
Я подумав, що ми можемо зробити щось подібне, не змінюючи нічого в HTML або JavaScript. Я дозволив собі включити повні назви місяців і числа замість назв днів, щоб було легше читати. Насолоджуйтесь!
- Розповсюдження контенту та PR на основі SEO. Отримайте посилення сьогодні.
- Платоблокчейн. Web3 Metaverse Intelligence. Розширені знання. Доступ тут.
- джерело: https://css-tricks.com/making-calendars-with-accessibility-and-internationalization-in-mind/
- :є
- $UP
- 1
- 11
- 2023
- 7
- 8
- 9
- 98
- a
- Здатний
- МЕНЮ
- про це
- вище
- абсолют
- доступність
- точно
- насправді
- Додатковий
- після
- ВСІ
- тільки
- вже
- та
- API
- Інтерфейси
- додаток
- вдячність
- підхід
- відповідний
- ЕСТЬ
- навколо
- масив
- статті
- AS
- призначений
- асоційований
- At
- Атрибути
- аудиторія
- автоматично
- фон
- заснований
- BE
- красивий
- оскільки
- перед тим
- Переваги
- КРАЩЕ
- між
- Великий
- Біт
- книга
- Box
- Перерва
- Розрив
- Створюємо
- by
- Календар
- call
- званий
- покликання
- CAN
- Може отримати
- випадок
- зміна
- заміна
- перевірка
- Перевірки
- код
- color
- Колонка
- COM
- загальний
- повний
- компонент
- заплутаний
- міркування
- містить
- зміст
- контекст
- Прохолодно
- може
- країна
- обкладинка
- покритий
- створювати
- Перетинати
- CSS
- Поточний
- виготовлений на замовлення
- дані
- Дата
- Дати
- день
- Днів
- глибше
- дефолт
- за замовчуванням
- певний
- визначаючи
- Залежно
- деталі
- Визначати
- розробників
- різний
- невідповідність
- дисплей
- документ
- Не знаю
- вниз
- малювати
- e
- кожен
- Раніше
- легше
- видання
- елемент
- Двигуни
- забезпечення
- Весь
- і т.д.
- Навіть
- точно
- приклад
- відмінно
- виняток
- існує
- додатково
- Падіння
- Фолс
- страх
- Feb
- лютого
- розібрався
- в кінці кінців
- кінець
- Перший
- виправляти
- потік
- для
- формат
- Четвертий
- п'ятниця
- від
- Повний
- функція
- Отримувати
- розрив
- отримати
- отримання
- даний
- дає
- Go
- буде
- добре
- сітка
- сітка-шаблон-стовпці
- Обробка
- Жорсткий
- Мати
- має
- допомога
- допомагає
- тут
- Виділіть
- надія
- Як
- HTML
- HTTPS
- i
- ідея
- ідеальний
- ідентифікує
- in
- включати
- інформація
- інформація
- початковий
- спочатку
- екземпляр
- замість
- Міжнародне покриття
- IT
- пунктів
- ітерація
- ЙОГО
- січня
- JavaScript
- тільки один
- Знати
- відомий
- МОВА
- мова
- останній
- макет
- догляд
- Li
- Liberty
- libraries
- як
- ліній
- пов'язаний
- список
- трохи
- Локалізація
- Довго
- подивитися
- шукати
- серія
- зробити
- РОБОТИ
- Робить
- управління
- багато
- Маржа
- математики
- макс-ширина
- просто
- Злиття
- метод
- може бути
- mind
- модифікований
- момент
- понеділок
- місяць
- місяців
- більше
- найбільш
- Mozilla
- ім'я
- Імена
- рідний
- навігація
- Необхідність
- Нові
- наступний
- Nuance
- номер
- номера
- об'єкт
- of
- on
- ONE
- онлайн
- Оптимізувати
- Інше
- інші
- інакше
- власний
- майданчик
- сторінка
- планета
- планування
- plato
- Інформація про дані Платона
- PlatoData
- положення
- позитивний
- це можливо
- досить
- первинний
- ймовірно
- процес
- правильний
- правильно
- власність
- за умови
- Поклавши
- Швидко
- швидше
- досягати
- Читати
- червонуватий
- регіон
- райони
- надання
- вимагається
- повертати
- Умови повернення
- показувати
- корінь
- ROW
- Правило
- s
- то ж
- Пошук
- Пошукові системи
- розділ
- обраний
- семантика
- окремий
- комплект
- установка
- налаштування
- сім
- зсув
- Короткий
- Повинен
- Показувати
- показаний
- Шоу
- аналогічний
- простий
- просто
- з
- один
- невеликий
- So
- solid
- деякі
- що в сім'ї щось
- Простір
- конкретно
- зазначений
- стояти
- стенди
- старт
- стан
- Крок
- Як і раніше
- стиль
- Super
- TAG
- Приймати
- шаблон
- Дякую
- Що
- Команда
- Їх
- Ці
- річ
- речі
- час
- times
- назва
- до
- сьогодні
- разом
- ТОТАЛЬНО
- до
- переводити
- правда
- Вівторок
- підручник
- типовий
- типово
- Оновити
- оновлений
- us
- використання
- значення
- Цінності
- версія
- W3
- шлях..
- способи
- week
- уїк-енд
- тижня
- ДОБРЕ
- Що
- Що таке
- Колесо
- Чи
- який
- в той час як
- волі
- вітер
- з
- в
- без
- чудовий
- слова
- Work
- працює
- світ
- світі
- б
- Загорнуті
- запис
- лист
- рік
- Ти
- зефірнет