Haben Sie schon einmal einen Kalender auf einer Webseite gesehen und sich gedacht: *Wie zum Teufel haben sie das gemacht*? Für so etwas ist es vielleicht naheliegend, zu einem Plugin oder sogar einem eingebetteten Google Kalender zu greifen, aber es ist tatsächlich viel einfacher, als Sie vielleicht denken, und erfordert nur das Dreigestirn aus HTML, CSS und JavaScript. Lassen Sie uns gemeinsam einen erstellen!
Ich habe ein Demo bei CodeSandbox eingerichtet, damit Sie sehen können, was wir anstreben.
Lassen Sie uns zuerst einige Anforderungen für die Funktionalität des Kalenders identifizieren. Er sollte
- Ein Monatsraster für einen bestimmten Monat anzeigen
- Daten aus den vorherigen und nächsten Monaten anzeigen, damit das Raster immer gefüllt ist
- Das aktuelle Datum anzeigen
- Den Namen des aktuell ausgewählten Monats anzeigen
- Zur vorherigen und nächsten Monat navigieren
- Dem Benutzer erlauben, mit einem einzigen Klick zum aktuellen Monat zurückzukehren
Oh, und wir werden dies als Single-Page-Anwendung erstellen, die Kalenderdaten von Day.js abruft, einer superleichten Utility-Bibliothek.
Wir werden uns darauf beschränken, kein bestimmtes Framework zu wählen, um die Dinge einfach zu halten. Für dieses Setup verwende ich Parcel für das Paketmanagement, damit ich in Babel schreiben, Dinge bündeln und die einzige Abhängigkeit für das Projekt verwalten kann. Schauen Sie sich die Datei package.json an bei CodeSandbox für Details.
Schritt 1: Beginnen Sie mit dem grundlegenden Markup und den Stilen
Lassen Sie uns mit der Erstellung einer grundlegenden Vorlage für unseren Kalender beginnen. Dies muss nichts Ausgefallenes sein. Es sollte aber auch ohne Tabellen erfolgen.
Wir können unser Markup in drei Schichten gliedern, wobei wir haben:
- Einen Bereich für den Kalenderkopf. Hier wird der aktuell ausgewählte Monat und die Elemente für die Monatsnavigation angezeigt.
- Einen Bereich für den Kalenderrasterkopf. Auch hier greifen wir nicht zu Tabellen, aber dies wäre quasi eine Tabellenüberschrift, die eine Liste mit den Wochentagen, beginnend mit Montag, enthält.
- Das Kalenderraster. Sie wissen schon, jeder Tag des aktuellen Monats, dargestellt als Quadrat im Raster.
Schreiben wir dies in eine Datei namens index.js. Diese kann in einem Ordner src im Projektordner liegen. Wir werden tatsächlich eine index.html-Datei im Stammverzeichnis des Projekts haben, die unsere Arbeit importiert, aber das primäre Markup wird in der JavaScript-Datei leben.
<!-- index.js -->
document.getElementById("app").innerHTML = `
<!-- Parent container for the calendar month -->
<div class="calendar-month">
<!-- The calendar header -->
<section class="calendar-month-header">
<!-- Month name -->
<div
id="selected-month"
class="calendar-month-header-selected-month"
>
July 2020
</div>
<!-- Pagination -->
<div class="calendar-month-header-selectors">
<span id="previous-month-selector"><</span>
<span id="present-month-selector">Today</span>
<span id="next-month-selector">></span>
</div>
</section>
<!-- Calendar grid header -->
<ol
id="days-of-week"
class="day-of-week"
>
<li>Mon</li>
...
<li>Sun</li>
</ol>
<!-- Calendar grid -->
<ol
id="calendar-days"
class="date-grid"
>
<li class="calendar-day">
<span>
1
</span>
...
<span>
29
</span>
</li>
</ol>
</div>
`;
Lassen Sie uns diese Datei in die index.html-Datei importieren, die sich im Stammverzeichnis des Projekts befindet. Hier passiert nichts Besonderes. Es ist lediglich eine HTML-Basis mit einem Element, das von unserer App angesprochen wird, und registriert unsere index.js-Datei.
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="src/index.js"></script>
</body>
</html>
Jetzt, da wir Markup zum Arbeiten haben, stylen wir es ein wenig, damit wir einen guten visuellen Startpunkt haben. Insbesondere werden wir
- Die Elemente mit Flexbox positionieren
- Einen Kalenderrahmen mit CSS Grid erstellen
- Die Beschriftungen innerhalb der Zellen positionieren
Zuerst erstellen wir eine neue Datei styles.css im selben src-Ordner, in dem wir index.js haben, und fügen dies ein:
body {
--grey-100: #e4e9f0;
--grey-200: #cfd7e3;
--grey-300: #b5c0cd;
--grey-800: #3e4e63;
--grid-gap: 1px;
--day-label-size: 20px;
}
.calendar-month {
position: relative;
/* Color of the day cell borders */
background-color: var(--grey-200);
border: solid 1px var(--grey-200);
}
/* Month indicator and selectors positioning */
.calendar-month-header {
display: flex;
justify-content: space-between;
background-color: #fff;
padding: 10px;
}
/* Month indicator */
.calendar-month-header-selected-month {
font-size: 24px;
font-weight: 600;
}
/* Month selectors positioning */
.calendar-month-header-selectors {
display: flex;
align-items: center;
justify-content: space-between;
width: 80px;
}
.calendar-month-header-selectors > * {
cursor: pointer;
}
/* | Mon | Tue | Wed | Thu | Fri | Sat | Sun | */
.day-of-week {
color: var(--grey-800);
font-size: 18px;
background-color: #fff;
padding-bottom: 5px;
padding-top: 10px;
}
.day-of-week,
.days-grid {
/* 7 equal columns for weekdays and days cells */
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.day-of-week > * {
/* Position the weekday label within the cell */
text-align: right;
padding-right: 5px;
}
.days-grid {
height: 100%;
position: relative;
/* Show border between the days */
grid-column-gap: var(--grid-gap);
grid-row-gap: var(--grid-gap);
border-top: solid 1px var(--grey-200);
}
.calendar-day {
position: relative;
min-height: 100px;
font-size: 16px;
background-color: #fff;
color: var(--grey-800);
padding: 5px;
}
/* Position the day label within the day cell */
.calendar-day > span {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 2px;
width: var(--day-label-size);
height: var(--day-label-size);
}
Der entscheidende Teil, der unser Raster einrichtet, ist dieser:
.day-of-week,
.days-grid {
/* 7 equal columns for weekdays and days cells */
display: grid;
grid-template-columns: repeat(7, 1fr);
}
Beachten Sie, dass sowohl der Kalenderrasterkopf als auch das Kalenderraster selbst CSS Grid für das Layout verwenden. Wir wissen, dass es immer sieben Tage in der Woche gibt, was es uns ermöglicht, die Funktion repeat() zu verwenden, um sieben Spalten zu erstellen, die proportional zueinander sind. Wir deklarieren auch eine min-height von 100px für jeden Tag im Kalender, um sicherzustellen, dass die Zeilen konsistent sind.
Wir müssen diese Stile mit dem Markup verknüpfen, also fügen wir dies am Anfang unserer index.js-Datei hinzu:
import "./styles.css";
Dies ist ein guter Punkt, um innezuhalten und zu sehen, was wir bisher haben.
Schritt 2: Einrichten des aktuellen Monatskalenders
Wie Sie wahrscheinlich bemerkt haben, enthält die Vorlage im Moment nur statische Daten. Der Monat ist hartkodiert als Juli und auch die Tageszahlen sind hartkodiert. Hier kommt Day.js ins Spiel. Es liefert alle Daten, die wir benötigen, um Daten an den richtigen Wochentagen für einen bestimmten Monat mit echten Kalenderdaten richtig zu platzieren. Es ermöglicht uns, alles vom Anfangsdatum eines Monats bis zu allen Datumsformatierungsoptionen zu erhalten und festzulegen, die wir für die Anzeige der Daten benötigen.
Wir werden
- Den aktuellen Monat abrufen
- Berechnen, wo die Tage platziert werden sollen (Wochentage)
- Die Tage für die Anzeige von Daten aus den vorherigen und nächsten Monaten berechnen
- Alle Tage in einem einzigen Array zusammenfassen
Zuerst müssen wir Day.js importieren und alle statischen HTML-Elemente (ausgewählter Monat, Wochentage und Tage) entfernen. Das machen wir, indem wir dies zu unserer index.js-Datei hinzufügen, direkt über der Stelle, an der wir die Stile importiert haben:
import dayjs from "dayjs";
Wir werden uns auch auf ein paar Day.js-Plugins verlassen. WeekDay hilft uns, den ersten Tag der Woche festzulegen. Manche bevorzugen Sonntag als ersten Tag der Woche. Andere bevorzugen Montag. Selbst an manchen Stellen macht es Sinn, mit Freitag zu beginnen. Wir werden mit Montag beginnen.
Das Plugin weekOfYear gibt den numerischen Wert für die aktuelle Woche von allen Wochen im Jahr zurück. Es gibt 52 Wochen im Jahr, also würden wir sagen, dass die Woche, die am 1. Januar beginnt, die erste Woche des Jahres ist und so weiter.
Hier ist also, was wir direkt nach unseren Importanweisungen in index.js einfügen:
const weekday = require("dayjs/plugin/weekday");
const weekOfYear = require("dayjs/plugin/weekOfYear");
dayjs.extend(weekday);
dayjs.extend(weekOfYear);
Sobald wir die hartkodierten Kalenderwerte entfernt haben, sieht unsere index.js-Datei jetzt so aus:
import dayjs from "dayjs";
import "./styles.css";
const weekday = require("dayjs/plugin/weekday");
const weekOfYear = require("dayjs/plugin/weekOfYear");
dayjs.extend(weekday);
dayjs.extend(weekOfYear);
document.getElementById("app").innerHTML = `
<div class="calendar-month">
<section class="calendar-month-header">
<div
id="selected-month"
class="calendar-month-header-selected-month"
>
</div>
<div class="calendar-month-header-selectors">
<span id="previous-month-selector"><</span>
<span id="present-month-selector">Today</span>
<span id="next-month-selector">></span>
</div>
</section>
<ul
id="days-of-week"
class="day-of-week"
>
</ul>
<ul
id="calendar-days"
class="days-grid"
>
</ul>
</div>
`;
Lassen Sie uns nun ein paar Konstanten festlegen. Insbesondere wollen wir ein Array von Wochentagen erstellen (d. h. Montag, Dienstag, Mittwoch usw.):
const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
Dann möchten wir das aktuelle Jahr abrufen und es im YYYY-Format setzen:
const INITIAL_YEAR = dayjs().format("YYYY");
Und wir möchten den aktuellen Monat als Ausgangspunkt beim erstmaligen Laden des Kalenders festlegen, wobei M den Monat als numerischen Wert formatiert (z. B. Januar entspricht 1):
const INITIAL_MONTH = dayjs().format("M");
Lassen Sie uns nun unseren Kalenderrasterkopf mit den Wochentagen füllen. Zuerst greifen wir auf das entsprechende Element (#days-of-week) zu, dann iterieren wir durch unser WEEKDAYS-Array, erstellen für jedes Element ein Listenelement und setzen den Namen für jedes davon:
// Select the calendar grid header element
const daysOfWeekElement = document.getElementById("days-of-week");
// Loop through the array of weekdays
WEEKDAYS.forEach(weekday => {
// For each item in the array, make a list item element
const weekDayElement = document.createElement("li");
// Append a child element inside the list item...
daysOfWeekElement.appendChild(weekDayElement);
/// ...that contains the value in the array
weekDayElement.innerText = weekday;
});
Schritt 3: Erstellen des Kalenderrasters
Das war ziemlich unkompliziert, aber jetzt beginnt der eigentliche Spaß, da wir uns mit dem Kalenderraster beschäftigen werden. Halten wir kurz inne und überlegen, was wir wirklich tun müssen, um das richtig hinzubekommen.
Zuerst wollen wir, dass die Datumszahlen in den richtigen Wochentagspalten landen. Zum Beispiel ist der 1. Juli 2020 ein Mittwoch. Dort sollte die Datumszählung beginnen.
Wenn der erste des Monats auf einen Mittwoch fällt, bedeutet das, dass wir in der ersten Woche leere Rasterelemente für Montag und Dienstag haben werden. Der letzte Tag des Monats ist der 31. Juli, der auf einen Freitag fällt. Das bedeutet, dass Samstag und Sonntag in der letzten Woche des Rasters leer sein werden. Wir wollen diese mit den nachfolgenden und vorhergehenden Daten des vorherigen und nächsten Monats füllen, damit das Kalenderraster immer voll ist.

Tage für den aktuellen Monat erstellen
Um die Tage für den aktuellen Monat zum Raster hinzuzufügen, müssen wir wissen, wie viele Tage im aktuellen Monat existieren. Das können wir mit der Methode daysInMonth von Day.js abrufen. Lassen Sie uns dafür eine Hilfsmethode erstellen.
function getNumberOfDaysInMonth(year, month) {
return dayjs(`${year}-${month}-01`).daysInMonth()
}
Wenn wir das wissen, erstellen wir ein leeres Array mit einer Länge, die der Anzahl der Tage im aktuellen Monat entspricht. Dann map() wir dieses Array und erstellen für jeden Tag ein Tagesobjekt. Das von uns erstellte Objekt hat eine willkürliche Struktur, sodass Sie bei Bedarf weitere Eigenschaften hinzufügen können.
In diesem Beispiel benötigen wir jedoch eine Eigenschaft date, die verwendet wird, um zu prüfen, ob ein bestimmtes Datum der aktuelle Tag ist. Wir geben auch eine Eigenschaft dayOfMonth zurück, die als Beschriftung dient (z. B. 1, 2, 3 und so weiter). isCurrentMonth prüft, ob das Datum im aktuellen Monat liegt oder außerhalb davon. Wenn es außerhalb des aktuellen Monats liegt, werden wir diese entsprechend stylen, damit die Leute wissen, dass sie außerhalb des Bereichs des aktuellen Monats liegen.
function createDaysForCurrentMonth(year, month) {
return [...Array(getNumberOfDaysInMonth(year, month))].map((day, index) => {
return {
date: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: true
};
});
}
Daten aus dem Vormonat zum Kalenderraster hinzufügen
Um Daten aus dem Vormonat für die Anzeige im aktuellen Monat zu erhalten, müssen wir prüfen, welcher Wochentag der erste Tag des ausgewählten Monats ist. Hierfür können wir das WeekDay-Plugin für Day.js verwenden. Lassen Sie uns dafür eine Hilfsmethode erstellen.
function getWeekday(date) {
return dayjs(date).weekday()
}
Dann müssen wir auf dieser Basis prüfen, welcher Tag der letzte Montag im Vormonat war. Diesen Wert benötigen wir, um zu wissen, wie viele Tage aus dem Vormonat in der aktuellen Monatsansicht sichtbar sein sollen. Diesen Wert können wir erhalten, indem wir vom Wochentagswert des ersten Tages des aktuellen Monats abziehen. Wenn beispielsweise der erste Tag des Monats ein Mittwoch ist, müssen wir 3 Tage abziehen, um den letzten Montag des Vormonats zu erhalten. Mit diesem Wert können wir ein Array von Tagesobjekten erstellen, beginnend mit dem letzten Montag des Vormonats bis zum Ende dieses Monats.
function createDaysForPreviousMonth(year, month) {
const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date);
const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
// Account for first day of the month on a Sunday (firstDayOfTheMonthWeekday === 0)
const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday ? firstDayOfTheMonthWeekday - 1 : 6
const previousMonthLastMondayDayOfMonth = dayjs(
currentMonthDays[0].date
).subtract(visibleNumberOfDaysFromPreviousMonth, "day").date();
return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((day, index) => {
return {
date: dayjs(
`${previousMonth.year()}-${previousMonth.month() + 1}-${previousMonthLastMondayDayOfMonth + index}`
).format("YYYY-MM-DD"),
dayOfMonth: previousMonthLastMondayDayOfMonth + index,
isCurrentMonth: false
};
});
}
Daten aus dem nächsten Monat zum Kalenderraster hinzufügen
Nun machen wir das Gegenteil und berechnen, welche Tage wir aus dem nächsten Monat benötigen, um das Raster für den aktuellen Monat zu füllen. Glücklicherweise können wir dieselbe Hilfsmethode verwenden, die wir gerade für die Berechnung des Vormonats erstellt haben. Der Unterschied besteht darin, dass wir berechnen, wie viele Tage aus dem nächsten Monat sichtbar sein sollen, indem wir diesen numerischen Wochentagswert von 7 abziehen.
Wenn beispielsweise der letzte Tag des Monats ein Samstag ist, müssen wir 1 Tag von 7 abziehen, um ein Array von benötigten Daten aus dem nächsten Monat (Sonntag) zu erstellen.
function createDaysForNextMonth(year, month) {
const lastDayOfTheMonthWeekday = getWeekday(`${year}-${month}-${currentMonthDays.length}`)
const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday ? 7 - lastDayOfTheMonthWeekday : lastDayOfTheMonthWeekday
return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
return {
date: dayjs(`${year}-${Number(month) + 1}-${index + 1}`).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: false
}
})
}
Okay, wir wissen, wie wir alle benötigten Tage erstellen, lassen Sie uns die gerade erstellten Methoden verwenden und dann alle Tage zu einem einzigen Array zusammenführen, das alle Tage enthält, die wir im aktuellen Monat anzeigen möchten, einschließlich Fülldaten aus dem vorherigen und nächsten Monat.
let currentMonthDays = createDaysForCurrentMonth(INITIAL_YEAR, INITIAL_MONTH)
let previousMonthDays = createDaysForPreviousMonth(INITIAL_YEAR, INITIAL_MONTH, currentMonthDays[0])
let nextMonthDays = createDaysForNextMonth(INITIAL_YEAR, INITIAL_MONTH)
let days = [...this.previousMonthDays, ...this.currentMonthDays, ...this.nextMonthDays]
Hier ist alles, was wir gerade abgedeckt haben, zusammengefasst in index.js:
// Same as before ...
const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const INITIAL_YEAR = dayjs().format("YYYY");
const INITIAL_MONTH = dayjs().format("M");
const daysOfWeekElement = document.getElementById("days-of-week");
// Add weekdays to calendar header
WEEKDAYS.forEach(weekday => {
const weekDayElement = document.createElement("li");
daysOfWeekElement.appendChild(weekDayElement);
weekDayElement.innerText = weekday;
});
let currentMonthDays = createDaysForCurrentMonth(INITIAL_YEAR, INITIAL_MONTH);
let previousMonthDays = createDaysForPreviousMonth(INITIAL_YEAR, INITIAL_MONTH);
let nextMonthDays = createDaysForNextMonth(INITIAL_YEAR, INITIAL_MONTH);
let days = [...previousMonthDays, ...currentMonthDays, ...nextMonthDays];
console.log(days);
function getNumberOfDaysInMonth(year, month) {
return dayjs(`${year}-${month}-01`).daysInMonth();
}
function createDaysForCurrentMonth(year, month) {
return [...Array(getNumberOfDaysInMonth(year, month))].map((day, index) => {
return {
date: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: true
};
});
}
function createDaysForPreviousMonth(year, month) {
const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date);
const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
// Cover first day of the month being sunday (firstDayOfTheMonthWeekday === 0)
const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday
? firstDayOfTheMonthWeekday - 1
: 6;
const previousMonthLastMondayDayOfMonth = dayjs(currentMonthDays[0].date)
.subtract(visibleNumberOfDaysFromPreviousMonth, "day")
.date();
return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((day, index) => {
return {
date: dayjs(
`${previousMonth.year()}-${previousMonth.month() +
1}-${previousMonthLastMondayDayOfMonth + index}`
).format("YYYY-MM-DD"),
dayOfMonth: previousMonthLastMondayDayOfMonth + index,
isCurrentMonth: false
};
});
}
function createDaysForNextMonth(year, month) {
const lastDayOfTheMonthWeekday = getWeekday(
`${year}-${month}-${currentMonthDays.length}`
);
const nextMonth = dayjs(`${year}-${month}-01`).add(1, "month");
const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday
? 7 - lastDayOfTheMonthWeekday
: lastDayOfTheMonthWeekday;
return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
return {
date: dayjs(
`${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`
).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: false
};
});
}
function getWeekday(date) {
return dayjs(date).weekday();
}
Schritt 4: Kalenderdaten anzeigen
Okay, wir haben also das grundlegende Markup für unseren Kalender, die Daten, die wir benötigen, um Daten aus dem aktuellen Monat anzuzeigen, sowie Daten aus dem vorherigen und nächsten Monat, um leere Rasterelemente zu füllen. Jetzt müssen wir die Daten zum Kalender hinzufügen!
Wir haben bereits einen Container für das Kalenderraster, #calendar-days. Holen wir uns dieses Element.
const calendarDaysElement = document.getElementById("calendar-days");
Lassen Sie uns nun eine Funktion erstellen, die einen Tag zu unserer Kalenderansicht hinzufügt.
function appendDay(day, calendarDaysElement) {
const dayElement = document.createElement("li");
const dayElementClassList = dayElement.classList;
// Generic calendar day class
dayElementClassList.add("calendar-day");
// Container for day of month number
const dayOfMonthElement = document.createElement("span");
// Content
dayOfMonthElement.innerText = day.dayOfMonth;
// Add an extra class to differentiate current month days from prev/next month days
if (!day.isCurrentMonth) {
dayElementClassList.add("calendar-day--not-current");
}
// Append the element to the container element
dayElement.appendChild(dayOfMonthElement);
calendarDaysElement.appendChild(dayElement);
}
Beachten Sie, dass wir eine Prüfung für die Daten durchführen, die aus den vorherigen und nächsten Monaten stammen, damit wir eine Klasse hinzufügen können, um sie anders als die Daten im aktuellen Monat zu stylen.
.calendar-day--not-current {
background-color: var(--grey-100);
color: var(--grey-300);
}
Das war's! Unser Kalender sollte jetzt so aussehen, wie wir es wollten.
Schritt 5: Aktuellen Monat auswählen
Was wir bisher haben, ist ziemlich gut, aber wir möchten, dass der Benutzer vorwärts und rückwärts im Zeitverlauf durch die Monate blättern kann, beginnend mit dem aktuellen Monat. Wir haben den Großteil der Logik bereits implementiert, daher müssen wir nur noch einen click-Listener zu den Paginierungsbuttons hinzufügen, der die Tagesberechnung neu ausführt und den Kalender mit aktualisierten Daten neu zeichnet.
Bevor wir beginnen, definieren wir Variablen für die Daten, die im aktuellen Monat, Vormonat und nächsten Monat liegen, damit wir sie im gesamten Code referenzieren können.
let currentMonthDays;
let previousMonthDays;
let nextMonthDays;
Lassen Sie uns nun eine Methode erstellen, die für die Neuberechnung der Kalendertage und das erneute Rendern des Kalenders beim Blättern zu einem anderen Monat verantwortlich ist. Wir werden diese Funktion createCalendar nennen. Diese Methode akzeptiert zwei Attribute – Jahr und Monat – und basierend darauf wird der Kalender mit neuen Daten neu gerendert, ohne dass eine neue Seite geladen wird.
Die Methode ersetzt den Inhalt des Headers, um immer die Beschriftung des ausgewählten Monats anzuzeigen.
function createCalendar(year = INITIAL_YEAR, month = INITIAL_MONTH) {
document.getElementById("selected-month").innerText = dayjs(
new Date(year, month - 1)
).format("MMMM YYYY");
// ...
Dann greift sie auf den Kalendertage-Container zu und entfernt alle vorhandenen Tage.
// ...
const calendarDaysElement = document.getElementById("calendar-days");
removeAllDayElements(calendarDaysElement);
// ...
Wenn der Kalender geleert ist, berechnet er neue Tage, die mit den zuvor erstellten Methoden angezeigt werden sollen.
//...
currentMonthDays = createDaysForCurrentMonth(
year,
month,
dayjs(`${year}-${month}-01`).daysInMonth()
);
previousMonthDays = createDaysForPreviousMonth(year, month);
nextMonthDays = createDaysForNextMonth(year, month);
const days = [...previousMonthDays, ...currentMonthDays, ...nextMonthDays];
// ...
Und schließlich fügt sie für jeden Tag ein Tageselement hinzu.
// ...
days.forEach(day => {
appendDay(day, calendarDaysElement);
});
Es fehlt noch ein Stück Logik: eine Methode removeAllDayElements, die den vorhandenen Kalender leert. Diese Methode nimmt das erste Kalendertageselement, entfernt es und ersetzt es durch ein weiteres. Von dort aus wird die Logik in einer Schleife ausgeführt, bis alle Elemente entfernt sind.
function removeAllDayElements(calendarDaysElement) {
let first = calendarDaysElement.firstElementChild;
while (first) {
first.remove();
first = calendarDaysElement.firstElementChild;
}
}
Jetzt können wir diese Logik wiederverwenden, wenn wir den Monat wechseln möchten. Erinnern Sie sich an den ersten Schritt, als wir eine statische Vorlage für unsere Komponente erstellt haben. Wir haben diese Elemente hinzugefügt:
<div class="calendar-month-header-selectors">
<span id="previous-month-selector"><</span>
<span id="present-month-selector">Today</span>
<span id="next-month-selector">></span>
</div>
Dies sind die Steuerelemente für die Monatsnavigation. Um sie zu ändern, müssen wir den aktuell ausgewählten Monat speichern. Lassen Sie uns eine Variable erstellen, um zu verfolgen, was das ist, und ihren Anfangswert auf den aktuellen Monat setzen.
let selectedMonth = dayjs(new Date(INITIAL_YEAR, INITIAL_MONTH - 1, 1));
Nun, um die Selektoren funktionsfähig zu machen, benötigen wir etwas JavaScript. Um es lesbarer zu machen, werden wir eine weitere Methode namens initMonthSelectors erstellen und die Logik dort aufbewahren. Diese Methode fügt Ereignis-Listener zu den Selektor-Elementen hinzu. Sie wird auf Klick-Ereignisse hören und den Wert von selectedMonth auf den Namen des neu ausgewählten Monats aktualisieren, bevor die Methode createCalendar mit den richtigen Jahr- und Monatswerten ausgeführt wird.
function initMonthSelectors() {
document
.getElementById("previous-month-selector")
.addEventListener("click", function() {
selectedMonth = dayjs(selectedMonth).subtract(1, "month");
createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
});
document
.getElementById("present-month-selector")
.addEventListener("click", function() {
selectedMonth = dayjs(new Date(INITIAL_YEAR, INITIAL_MONTH - 1, 1));
createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
});
document
.getElementById("next-month-selector")
.addEventListener("click", function() {
selectedMonth = dayjs(selectedMonth).add(1, "month");
createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
});
}
Das war's! Unser Kalender ist fertig. Das ist zwar großartig, aber es wäre noch schöner, wenn wir das aktuelle Datum hervorheben könnten, damit es sich vom Rest abhebt. Das sollte nicht sehr schwer sein. Wir stylen bereits Tage, die nicht im ausgewählten Monat liegen, also machen wir etwas Ähnliches.
Wir erstellen eine Variable, die für heute gesetzt ist:
const TODAY = dayjs().format("YYYY-MM-DD");
Dann müssen wir in der Methode appendDay, wo wir eine Klasse für Tage außerhalb des aktuellen Monats anwenden, eine weitere Prüfung hinzufügen, ob das Element das heutige Datum ist. Wenn ja, fügen wir diesem Element eine Klasse hinzu:
function appendDay(day, calendarDaysElement) {
// ...
if (day.date === TODAY) {
dayElementClassList.add("calendar-day--today");
}
}
Jetzt können wir es stylen!
.calendar-day--today {
padding-top: 4px;
}
.calendar-day--today > div {
color: #fff;
border-radius: 9999px;
background-color: var(--grey-800);
}
Voilà, da haben wir es! Schauen Sie sich die finale Demo an, um alles zusammengefügt zu sehen.
Wow! Funktioniert gut und ist leicht zu erweitern (Feiertage, Klicks)
Das funktioniert super, mit einer Ausnahme: der Mobilansicht. Gibt es Pläne, dies hinzuzufügen?
Hey Mica, gute Frage! Das HTML für diese Demo ist ziemlich willkürlich – wichtig sind die Daten, die hineingeladen werden. Es gibt eine responsive Kalender-Starter-Vorlage in unserem Snippets-Bereich, die ein guter Ausgangspunkt sein könnte.
Wirklich nett! Aber einige Lücken zwischen den Zellen erscheinen größer als andere. Wenn ich die Fenstergröße ändere, ändern sich die größeren Lücken. Gibt es dafür eine Umgehungslösung?
Wenn etwas endlich perfekt für eine Tabelle ist, benutzen sie keine Tabelle…
Wenn ich versuche, index.html aus dem abgeschlossenen Projekt in Firefox zu öffnen, erhalte ich diese Fehlermeldung:
Uncaught SyntaxError: import declarations may only appear at top level of a module,
was auf diese Zeile in index.js hinweist:
import dayjs from “dayjs”;
Außerdem habe ich dayjs-1.10.3.jar von Maven, bin mir aber nicht sicher, wie ich es mit dem Kalender index.js verknüpfen kann.
Schön! Gibt es eine Funktion, um ein Array von Ereignissen hinzuzufügen?
Hallo Samuele,
konnten Sie Ereignisse hinzufügen?