Как сделать календарь в html и css

Добавил пользователь Владимир З.
Обновлено: 04.10.2024

При создании различных веб-сервисов работа с датами и временем практически всегда занимает весомое место. Создание календаря - достаточно простая задача, которая, тем не менее, часто вызывает затруднения в реализации у начинающих разработчиков.

Сегодня мы создадим виджет простого месячного календаря, отвечающий следующим условиям:

  1. по умолчанию загружается текущий месяц,
  2. текущая дата выделяется,
  3. возможно смещение по календарю на месяц вперед или назад.

Содержание

Создадим контейнер обертку month-calendar, в котором разместим три блока:

  1. month: содержит наименования месяца и года, а также элементы навигации,
  2. weekdays: наименования дней недели,
  3. days: собственно, сам календарь (табличная часть, содержит дни месяца).

Так выглядит html-разметка основных блоков календаря:

Обратите внимание: несмотря на визуальную реализацию виджета в виде таблицы, для каждого из вышеперечисленных дочерних элементов контейнера month-calendar мы используем немаркированный список ul . Соответственно, в дальнейшем мы будем манипулировать элементами списков li .

А вот и финальная верстка:

В данном случае для элементов навигации нужно подключить библиотеку Font Awesome (Web Font или SVG версию).

В основе позиционирования элементов календаря находится модель flexbox.

На что обратить внимание:

  1. ширина контейнера month-calendar равна 100%: width: 100% ,
  2. для элементов навигации prev и next используются свойства float: left и float-right ,
  3. блок days имеет фиксированную высоту height: 14rem , что позволяет избежать смещения или наложения элементов при динамической смене данных виджета,
  4. для табличной части календаря элементы li располагаются слева направо и сверху вниз: горизонтальное выравнивание - justify-content: left , вертикальное - align-content: flex-start ,
  5. в одном ряду табличной части находится семь элементов, это достигается за счет использования выражения calc: flex: 0 0 calc(100% / 7) ,
  6. для исключения влияния размеров отсутпов на расчетные значения для всех элементов установлено свойство box-sizing: border-box .

Основу работы виджета составляют свойства и методы встроенного объекта Date.

Экземпляр объекта Date

создать новый экземпляр объекта Date можно только через конструктор:

Вызов Date() как функции (без new) вернет не экземпляр объекта, а строку.

Как получить год, месяц, дату?

Чтобы получить год, месяц, дату экземпляра объетка Date, обратимся к встроенным методам:

Как получить название месяца?

Чтобы получить назавние месяца воспользуемся массивом:

Как получить текущий день недели?

Для определения текущего дня недели тоже существует встроенная функция:

Обратие внимание: отсчет дней недели идет с воскресенья, которое равно 0.

Как получить количество дней в месяце?

Существует небольшая хитрость, основанная на способности методов объекта Date автоматически пересчитывать параметры, приводя их к корректному значению. Так, если мы установим дату экземпляра объекта равной 0, метод getDate() вернет дату последнего дня предыдущего месяца, то есть полученное значение будет равнятся количеству дней в предыдущем месяце:

Как получить количество дней недели до начала текущего месяца.

Если неделя стартует с воскресенья, для определения количества дней недели до начала текущего месяца достаточно получить номер первого дня текущего месяца:

В случае, когда неделя начинается с понедельника, нужно получит номер последнего дня недели предыдущего месяца:

Как корректно сместить дату на месяц назад или вперед?

Мы опять обращаемся к способности методов объекта Date автоматически корректировать дату:

Итоговый JS-файл

Опираясь на данные, полученные выше создадим итоговый код JavaScript:

Результат

А вот и пример на codepen:

Спасибо за внимание.

Photo by CHUTTERSNAP on Unsplash

Идет 2018 год, модные пацаны давно уже верстают на grid, а я все на третьем бутстрапе сижу с col-md кочерячусь, мельком поглядывая на четвертый.

Решил я, что это не дело, и стоит немного знания освежить, но у grid вроде как поддержка пока хромает, а вот flex технологию уже даже утюги поддерживают.

Вот и решил его освоить. И процессом усвоения с вами поделится. В общем, будем верстать календарик на весь год.

Результат будет выглядеть примерно вот так:


Так получилось, что я адепт vue, и поэтому для рендера буду использовать его. Для работы с датами буду использовать библиотечку moment, можно и без нее обойтись, но мне с ней привычнее.

Подготовка

Устанавливаем vue-cli, если у вас её еще нет:

создаем проект на базе шаблона webpack-simple, я буду использовать scss (в основном для комментов), поэтому когда визард спросит вас

ответьте y(es), в общем запускаем:

добавим библиотечку moment.js

очищаем файлик App.vue

Чтоб не показаться варваром, не будем просто верстать голый календарь, а разработаем отдельную компоненту.

Создадим файлик Calendar.vue:

подключим компоненту глобально в main.js

добавим компоненту в App.vue

Если все верно сделали, то увидим слово "Календарь" на белом фоне.

Готовим данные

Прежде чем что-то рисовать надо подготовить данные для календаря. Я предлагаю упихать данные по году в массив из месяцев. В свою очередь, каждый месяц будет представлять собой объект вида:

то бишь название месяца и массив из недель. Каждая неделя будет представлять собой объект где к каждому дню (от 1 до 7) будет привязана дата и может еще какая-нибудь мета информация:

переключимся на файлик Calendar.vue, и обновим часть ответственную за скрипт:

Можно заглянуть в vue-devtools и увидеть там:


Верстаем

Ну давайте чего-нибудь уже выведем. Сначала научимся верстать один месяц, а потом, как освоимся, выведем все остальные. В общем, правим шаблон Calendar.vue:

Сначала заставим отображать даты в нашей неделе в ряд, для этого поправим стиль:


теперь укажем, что каждый день должен занимать одинаковое количество места в нашем ряду:

Ну вроде поприличнее стало, только цифры таки скачут. Происходит это потому, что flex-grow по сути распределяет пустое пространство, а текст цифр в это пустое пространство не входит, поэтому, чтобы ячейки с цифрами стали действительно равными надо указать в стиле, чтобы ширина текста не учитывалась. Для этого установим свойству flex-basis на ноль.

Если не совсем понятно что я говорю, попробуйте поизучать данную картинку:

Ну как? Правим стиль:

от теперь красота


Я думаю мы теперь готовы к тому, чтобы попробовать вывести все месяцы, правим шаблон:

Отлично, у нас уже своего рода респонсивный календарь:

Но нам этого мало, у нас календарь отображается в столбик, как завещал дедушка div, а нам бы в строчку… Сделаем по аналоги. Только что мы каждую неделю назначили flex контейнером для ее дней. А теперь наш блок year назначим flex контейнером для его месяцев. Добавим стили:

чет, каша какая-та:


причина сей каши в том, что по умолчанию flex не делает переносов, а пытается все отобразить в одну строчку, ну и соответственно сжимает покуда сил хватает, а их не хватает. Чтобы включить режим переносов, надо в нашем контейнер year добавить свойство flex-wrap, сделаем это:

Ну, эээ… типа получше стало, хотя б переносит:


Тут воздух маловато, но нам кстати так даже интереснее. Очевидно, что лучше всего чувствуют себя ноябрь и декабрь, но так как мы за равенство, то давайте ужмем их, чтобы сильно не выделялись.

Чтобы ужать, надо убрать flex-grow: 1 у month, (ток добавили, теперь удалять. ), который отвечает за растяжение в рамках строки:


За то как будут располагаться последние два (на самом деле не только за них) висящих элемента отвечает justify-content в стиле контейнере, по умолчанию он равен flex-start. Можно выровнять в конец.

Вот гифка с разными значениями:

Так как я планирую, что у меня будет всегда одинаковое количество месяцев в строке, и хочу чтобы они занимали все свободное место, то я пожалуй верну flex-grow: 1; обратно, и добавлю немного воздуха:


Еще раз вернусь к justify-content и flex-grow: 1. Сравните две гифки, на первой у month flex-grow = 1, на второй — свойство отсутствует:

Какой вариант вам больше по душе, решайте сами.

Добавим строчку с днями недели. Сначала добавим вычислимое свойство в скрипт

а теперь отобразим их в шаблоне:


Я хочу чтобы воскресенье у меня было красненькое, давайте добавим динамический стиль к узлу .day:

А теперь подкорректируем стили, чуток красоты наведем:


Ну и последние штрихи: добавим возможность менять год и сделаем фиксированный заголовок средствами flex.

Переключимся на App.vue файл, и откорректируем шаблон:

добавилась строчка с годом, пока, как видно, не фиксированная:

Подправим стили в App.vue, уберем отступы в body, установим высоту html и body на всю высоту окна, и сделаем заголовок покрасивше, я намеренно использую два узла style, один для глобальных стилей второй для локальных:

Идея создания фиксированного заголовка на flex заключается в использовании двух вложенных контейнеров flex, один из которых ограничивает высоту всего содержимого, а второй, вложенный, использует flex-direction: column.

Классно, да? Вы можете даже сделать футер:

Ну и давайте кнопки для переключения года добавим:

Воспользуемся уже полученными знаниями, и сделаем заголовок более flex-образным, правим стили:


Хм… что-то тут не так. Чет наши заголовки прям сдавило и верстка поплыла. К сожалению это тот момент, который я не до конца понял почему так произошло. Но как я полагаю, это из-за того что display: flex задает динамическую высоту, и находясь внутри другого flex контейнера, ориентируется на размеры заданные своим родителем.

В общем, чтобы это вылечить, надо запретить flex контейнеру внутри которого находится наш header сжимать его размеры, для этого добавим свойство flex-shrink:

Ну вот и все, теперь у вас есть flex-календарь на любой год!


В это статье не удалось показать все возможности flex, но общий подход работы с ним, думаю, отразить получилось.

Я надеюсь, что статья поможет тем, кто как и я застрял в css-временах где-то между 3-м и 4-м бутстрапом, сделать свои первые шаги навстречу современному css.

Сетка календаря выглядит, как идеальный кандидат для использования CSS-grid. Ниже рассмотрим пример, как можно сделать разметку для календарика с помощью всего нескольких строк кода. В итоге должно получиться так:

Calendar layout based on CSS-grid

Начать следует с HTML-разметки:

В нормальных условиях обычно не приходится вручную писать все числа

. Эта часть легко может быть сгенерирована с помощью Javascript в браузере или сервером, поэтому для примера будет использоваться только HTML и CSS.

В разметке будут две части: контейнер для дней недели .days-of-week-container и контейнер для чисел месяца .calendar-container . Оба будут реализованы с помощью CSS-grid, а текст будет выравниваться по правому краю:

Теперь, если посмотреть на сетку, станет ясно, что здесь будет 7 колонок, поэтому первое, что приходит в голову, написать как-то вроде:

Но это слишком примитивно и некрасиво, поэтому для настройки наших колонок будем использовать repeat :

Теперь, дни недели и сам календарь на месяц будут располагаться в макете сетки, состоящей из 7 колонок.

Что делать, если месяц должен начинаться не в понедельник, а например, в пятницу? Достаточно использовать CSS-свойство grid-column и переместить первый день месяца в колонку любого дня недели в календаре, а автоматическое размещение позаботится обо всем остальном.

Для установки значения grid-column можно использовать настраиваемые CSS-свойства и управлять расположением первого дня месяца из Javascript. Как это сделать, можно посмотреть на странице о передаче данных между CSS и JavaScript с помощью CSS-переменных.

Это всё, что требуется для реализации простого макета календаря: CSS-grid и всего несколько строк кода.

Что нам понадобится

На самом деле в плане навыков для выполнения задания этого руководства вам понадобится не так и много — просто редактор кода (задействуйте CodePen, чтобы упростить себе задачу) и некоторые базовые знания по HTML и CSS. Однако вам нужны будут определенные визуальные материалы: я использовал очень красивые иллюстрации из Envato Elements (они векторные и абсолютно подходят для наших задач):




Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

1. Добавление сетки

В нашем праздничном календаре мы будем использовать 25 элементов сетки; по одному на каждый из дней, предшествующих Рождеству, и один — для титульного рисунка. На маленьких экранах это будет выглядеть примерно так:


А так это будет выглядеть на больших экранах:


Бирюзовой элемент сетки будет содержать заголовок.

Разметка

В целом нам понадобятся 25 элементов в контейнере, поэтому давайте добавим соответствующий HTML-код:

Совет. Учитывая повторяющийся характер этой разметки, вы можете предпочесть использовать язык шаблонов, такой как HAML. HAML позволяет перебрать 24 элемента, чтобы не пописывать каждый из них. Следующий небольшой фрагмент кода компилируется в то, что вы видите выше:

Когда мы будем размещать другие элементы в этих дивах, вы по достоинству оцените время, которое экономит для вас HAML!

Основные стили Grid

Давайте теперь добавим основные стили, чтобы задать сетку. Начнем с назначения display: grid; для элемента контейнера. Затем, после некоторых измерений, мы определяем элементы сетки.

grid-template-columns: repeat(3, 1fr); дает нам три столбца, каждый из которых имеет равную ширину (часть 1fr)

grid-template-rows: auto; в принципе является значением по умолчанию, но это объявляет, что строки не будут иметь определенной высоты, мы можем добавить в них столько контента, сколько захотим, и они будут растягиваться вместе с ним.

grid-gap: 25px; определяет зазоры.

Это на самом деле все, что нам нужно. Grid этого уже достаточно, но мы конкретнее укажем, где должен размещаться каждый элемент. По этой причине мы будем использовать Grid Areas.

Grid Areas

Мы можем назначить имя для каждой области сетки, которую определили, и можем фактически прописать ее визуально:

Переходим к адаптивности

Добавив медиа-запрос, мы можем (очень просто) изменить макет для больших экранов.

С помощью этого медиа-запроса мы объявляем, что для окон просмотра шириной более 500 пикселей (произвольная цифра) мы изменяем сетку так, чтобы она имела шесть столбцов: grid-template-columns: repeat(6, 1fr);.

И мы можем полностью переопределить расположение элементов, разместив дни, где нам нравится. Вы заметите, что титульный блок теперь занимает три столбца и три строки в верхнем левом углу, а d24 занимает область 2 × 2, чтобы он больше выделялся. Это станет понятнее, когда на следующем шаге мы назначим элементы сетки для областей сетки.

Пока что мы не можем видеть ничего из того, что мы сделали, поэтому давайте добавим некоторые временные стили к элементам сетки, чтобы сделать их видимыми.

Назначение областей сетки

Теперь нам нужно назначить элементы сетки для областей, которые мы указали. Мы делаем это следующим образом:

Вы можете видеть, что div.title был назначили для области сетки t, и каждый день был назначен для соответствующей области. Излишне напоминать, что вы, конечно, не можете назначить .day-3 в grid-area: d16;, мы просто организуем элементы таким образом. Вот, как это выглядит?

2. Открытие дверей

Это базовая структура, теперь нам нужно сделать в CSS двери. Мы собираемся поместить в каждый элемент сетки два div — один для передней части двери, другой для задней части — оба обернуты в другой div, и мы используем переключатель чек-бокс, чтобы переворачивать элемент и открывать дверь при нажатии на определенный день. Начнем с добавления метки, чек-боксов и дивов для каждого элемента сетки:

Опять же, вам нужно сделать это для всех 24 элементов сетки. А это довольно большой кусок повторяющейся разметки длиной в 245 строк и большой кусок работы. Поэтому вы можете использовать HAML. Вот как это будет выглядеть:


Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

После небольших изменений полей теперь все чек-боксы выглядят так:

Двери

Приготовься: то, что мы сейчас будем делать, является, возможно, самым сложным. Это значительный объем работы, но без него мы практически ничего не сможем сделать из того, что нам нужно. Поэтому наберитесь терпения. Мы собираемся задать определенные изменения с помощью 3D-преобразований.

Сначала мы скрываем чек-боксы, затем объявляем, что хотим, чтобы к меткам применялся определенный уровень перспективы (это обрабатывает perspective: 1000px;). Дочерние элементы меток будут отображаться в трехмерном пространстве с помощью transform-style: preserve-3d;, а не просто на плоскости. Затем мы используем некоторые стили Flexbox, чтобы обеспечить, чтобы метка заполняла всю доступную для нее область.

Некоторые стили для элемента .door (который имеет переднюю и заднюю стороны), которые устанавливают время перехода и описывают дальнейшее состояние:

Читайте также: