Создание пользовательского приложения календаря - отличный способ углубить ваше понимание JavaScript, модели объекта документа браузера (DOM) и сохранения данных на стороне клиента. Используя API веб-хранилища - в частности, локальное хранилище - вы можете создать полностью функциональный календарь, управляемый событиями, который работает полностью в браузере без необходимости сервера бэкэнда или базы данных. Это руководство проведет вас через каждый шаг, от структурирования HTML до реализации расширенных функций, таких как управление событиями перетаскивания.

Зачем строить пользовательский календарь?

Предварительно построенные библиотеки календаря (такие как FullCalendar или DayPilot) предлагают быстрые решения, но создание одного с нуля дает вам полный контроль над пользовательским интерфейсом, обработкой данных и производительностью. Вы также получаете практический опыт работы с основными концепциями JavaScript, такими как объект , делегирование событий и API . Полученное приложение может быть расширено в инструмент управления проектами, систему бронирования или личный журнал.

Настройка структуры HTML

Начните с чистого HTML-скелета. Календарь нуждается в трех основных компонентах: навигационной панели (для меняющихся месяцев), контейнере сетки (для дневных ячеек) и форме ввода событий. Используйте элементы с семантическими названиями классов для легкого стиля и сценариев.

<div id="calendar">
 <div class="calendar-header">
 <button id="prevBtn">&larr;</button>
 <h2 id="monthYear"></h2>
 <button id="nextBtn">&rarr;</button>
 </div>
 <div class="calendar-grid"></div>
 <div class="event-form">
 <input type="text" id="eventInput" placeholder="Add an event..." />
 <button id="saveEvent">Save</button>
 </div>
</div>

будет динамически заселяться JavaScript. Для удобного для мобильных устройств макета рассмотрите возможность использования 7-колонной CSS-сети (по одной для каждого дня недели). Забронируйте первый ряд для имен дней (Sun-Sat).

Укладка календаря с помощью CSS

Чистый, отзывчивый CSS превращает сырой HTML в удобный интерфейс. Используйте CSS Grid для сетки календаря, настроив . Добавьте прокладки за несколько дней до первого месяца. Для доступности, убедитесь в достаточном цветовом контрасте и включите стили на интерактивных элементах. Ниже показан минимальный таблица стилей (встроенная или связанная).

.calendar-grid {
 display: grid;
 grid-template-columns: repeat(7, 1fr);
 gap: 2px;
}
.day-cell {
 min-height: 80px;
 border: 1px solid #ccc;
 padding: 4px;
 position: relative;
}
.day-cell.today {
 background-color: #eef;
}
.event {
 font-size: 0.8em;
 background: #4caf50;
 color: #fff;
 padding: 2px 4px;
 margin: 1px 0;
 border-radius: 3px;
 cursor: pointer;
}

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

Основной JavaScript: генерация календарной сети

Вся динамическая визуализация живет в JavaScript. Центральная функция, , вычисляет первый день месяца и общее количество дней. Используйте , чтобы получить индекс будни (0 = воскресенье). Затем генерируйте ячейки в цикле от 1 до последнего дня месяца.

Работа с объектом даты

Объект является основой календарной логики. Чтобы получить последний день месяца, используйте (день 0 следующего месяца дает последний день текущего месяца. Храните текущий месяц и год в глобальных переменных, чтобы навигация могла их обновить.

Построение сетки

function renderCalendar() {
 const grid = document.querySelector('.calendar-grid');
 grid.innerHTML = '';

 const firstDay = new Date(currentYear, currentMonth, 1).getDay();
 const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();

 // Add empty cells for days before the 1st
 for (let i = 0; i < firstDay; i++) {
 const empty = document.createElement('div');
 empty.className = 'day-cell empty';
 grid.appendChild(empty);
 }

 // Generate day cells
 for (let day = 1; day <= daysInMonth; day++) {
 const cell = document.createElement('div');
 cell.className = 'day-cell';
 cell.dataset.date = `${currentYear}-${currentMonth + 1}-${day}`;

 const dateSpan = document.createElement('span');
 dateSpan.textContent = day;
 cell.appendChild(dateSpan);

 // Highlight today
 const today = new Date();
 if (currentYear === today.getFullYear() && currentMonth === today.getMonth() && day === today.getDate()) {
 cell.classList.add('today');
 }

 // Load events for this date
 loadEvents(cell);

 grid.appendChild(cell);
 }
}

Обновить дисплей месяца/год в заголовке:

Навигация между месяцами

Прикрепите кнопки «Предыдущее» и «Следующее» к кнопкам события. Добавление или уменьшение , при необходимости корректируя год (например, после декабря придут в январе следующего года. После обновления переменных позвоните снова.

document.getElementById('prevBtn').addEventListener('click', () => {
 currentMonth--;
 if (currentMonth < 0) {
 currentMonth = 11;
 currentYear--;
 }
 renderCalendar();
});

document.getElementById('nextBtn').addEventListener('click', () => {
 currentMonth++;
 if (currentMonth > 11) {
 currentMonth = 0;
 currentYear++;
 }
 renderCalendar();
});

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

Хранение и удержание; извлечение событий с помощью локального хранилища

Местное хранилище обеспечивает простой магазин ключевых значений, который выживает при перезапуске браузера. Используйте последовательный шаблон ключа, например, , сохраняя JSON-массивы объектов событий. Включайте такие свойства, как , , и .

Спасти новое событие

Когда пользователь нажимает кнопку «Сохранить», улавливает значение ввода и выбранную в настоящее время дату. (Вы можете добавить датчик или определить, на какую ячейку дня нажата.) Восстановите любые существующие события для этой даты из Local Storage, нажмите новое событие, а затем сохраните обратно.

function saveEvent(dateKey, eventText) {
 const existing = JSON.parse(localStorage.getItem(dateKey)) || [];
 existing.push({ id: Date.now(), text: eventText, time: new Date().toLocaleTimeString() });
 localStorage.setItem(dateKey, JSON.stringify(existing));
 renderCalendar(); // refresh to show new event
}

Загрузка событий в дневные ячейки

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

function loadEvents(cell) {
 const dateKey = cell.dataset.date;
 const events = JSON.parse(localStorage.getItem(dateKey));
 if (!events) return;

 events.forEach(evt => {
 const eventDiv = document.createElement('div');
 eventDiv.className = 'event';
 eventDiv.textContent = evt.text;
 eventDiv.dataset.id = evt.id;
 // Optional: add a delete button
 cell.appendChild(eventDiv);
 });
}

Удаление и редактирование событий

Чтобы удалить, прикрепите событие клика к каждому элементу события (или используйте делегацию события). Восстановите массив для этой даты, отфильтровывайте событие по его и сохраните обновленный массив обратно в локальное хранилище. Редактирование может быть выполнено путем двойного щелчка по событию, подсказки для нового текста и обновления массива.

Расширенные возможности для повышения функциональности

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

Цветовые кодировки по типу

Разрешить пользователям назначать категории (например, работа, личная жизнь, здоровье). Хранить поле в локальном хранилище. В CSS определять классы, такие как , и применять их динамически.

Перепланировка Drag-and-Drop

Используйте HTML5 Drag и Drop API для перемещения событий между ячейками дня.Когда событие выпадает в другой день, обновите ключ даты в локальном хранилище: удалите событие из исходного массива дат и переместите его в массив новой даты.

Регулярные события (недельно / ежемесячно)

Добавьте опцию «Повторить» при сохранении события. В функции загрузки вычислите, соответствует ли текущая дата правилу повторения (например, каждый понедельник) и отображайте соответственно событие. Правила повторения в отдельном локальном ключе хранения, чтобы избежать дублирования.

Недельный вид и мини-календарь

Переключитесь между месяцами и неделями. В режиме недели отображаются только дни текущей недели с большим горизонтальным пространством. Небольшой мини-календарь для просмотра месяца помогает пользователям быстро перейти на любую дату.

Сделать календарь доступным и отзывчивым

Тестируйте календарь на различных размерах экрана. Используйте запросы , чтобы складывать дневные ячейки вертикально на очень маленьких экранах или уменьшать наложение. Убедитесь, что все интерактивные элементы доступны для клавиатуры: прикрепляйте события для входа и выхода. Используйте атрибуты на кнопках и живых областях для считывателей экрана, чтобы объявить о ежемесячных изменениях и дополнениях к событиям.

Советы по производительности и управлению данными

  • Batched Storage Writes: Вместо чтения/записи локального хранилища для каждой операции события кэшируйте события всего месяца в объекте JavaScript и периодически меняйте смыв.
  • Ограничить размер события: Держать объекты события маленькими. Избегайте хранения больших строк или двоичных данных.
  • Обратная связь для лимитов хранения : Локальное хранение обычно ограничено 5-10 МБ. Предупредите пользователей, когда хранилище почти заполнено.
  • Использовать индексированный DB для больших наборов данных : Если в вашем календаре будут проводиться тысячи событий, рассмотрите IndexedDB для повышения производительности и возможностей запроса.

Заключение

Создание пользовательского приложения календаря с JavaScript и Local Storage - это полезный проект, который обостряет ваши навыки интерфейса. Вы учитесь манипулировать DOM, работать с датами, управлять состоянием без сервера и обрабатывать взаимодействия с пользователем. Кодовая база может быть расширена с помощью синхронизации сервера, облачного резервного копирования или интеграции с внешними календарями через API Google Calendar. Начните с базовой сетки, добавьте настойчивость, а затем наложите на такие функции, как перетаскивание и повторяющиеся события. Результат - индивидуальный календарь, который ведет себя именно так, как вам нужно.

Для дальнейшего чтения, изучите документацию Storage API и Drag and Drop API на MDN. Счастливое кодирование!