Overview
The JavaScript Calendar component includes a zoom feature that can switch selected Calendar properties at runtime.
The main purpose of the Calendar zoom feature is to dynamically change the time scale. This is done by specifying a set of properties, such as cellDuration or timeHeaderCellDuration, for each zoom level.
In this tutorial, we will explore another use of the zoom feature: magnifying the Calendar grid and text for better visibility.
Three zoom levels are defined, each with a different magnification of the Calendar cells, headers, and event text.
Users can switch the zoom level using a slider displayed above the Calendar.
It is also possible to change the magnification level using the [-] and [+] buttons.
The project uses the open-source DayPilot Lite for JavaScript scheduling library (@daypilot/daypilot-lite-javascript).
Defining the Zoom Levels
We will define three zoom levels:
"100%"
"115%"
"130%"
The zoom levels are defined using the zoomLevels property of the Calendar component:
const dp = new DayPilot.Calendar("dp", {
// ...
zoomLevels: [
{
name: "100%",
properties: {
cellHeight: 20,
headerHeight: 30
}
},
{
name: "115%",
properties: {
cellHeight: 23,
headerHeight: 35
}
},
{
name: "130%",
properties: {
cellHeight: 26,
headerHeight: 40
}
}
]
});Each zoom level defines a set of Calendar properties and their values that will be applied when this level is selected.
Every level defines custom values for the cellHeight and headerHeight properties. In this example, we use fixed values, but it is also possible to replace them with a function that returns the desired value dynamically.
The
namevalue is custom metadata used by the application. It is not a DayPilot Calendar configuration property; you can include any custom values at this level. The sample displays thenamevalue in the toolbar label when describing the current zoom level.
The cellHeight value also indirectly affects the event box dimensions.
Adjusting the Calendar Font Size
The Calendar zoom API applies the properties object for the selected level. For text magnification, the sample also stores a CSS class name as custom metadata on each zoom level:
const dp = new DayPilot.Calendar("dp", {
// ...
zoomLevels: [
{
name: "100%",
cssClass: "zoom100",
properties: {
// ...
}
},
{
name: "115%",
cssClass: "zoom115",
properties: {
// ...
}
},
{
name: "130%",
cssClass: "zoom130",
properties: {
// ...
}
}
]
});The applyLevel() function shown later reads this cssClass value and applies it to the Calendar placeholder.
The zoom100, zoom115, and zoom130 classes define the font size for Calendar elements:
.zoom100 .calendar_default_colheader_inner { font-size: 13px; }
.zoom100 .calendar_default_rowheader_inner { font-size: 21px; }
.zoom100 .calendar_default_event_inner { font-size: 13px; }
.zoom115 .calendar_default_colheader_inner { font-size: 15px; }
.zoom115 .calendar_default_rowheader_inner { font-size: 24px; }
.zoom115 .calendar_default_event_inner { font-size: 15px; }
.zoom130 .calendar_default_colheader_inner { font-size: 17px; }
.zoom130 .calendar_default_rowheader_inner { font-size: 28px; }
.zoom130 .calendar_default_event_inner { font-size: 17px; }Slider Control

The slider control is defined using the standard <input type="range"> HTML element.
HTML:
<div class="calendar-toolbar">
<span>Magnification:</span>
<input type="range" min="0" max="2" step="1" id="zoomLevel" value="0" aria-label="Magnification level"/>
</div>In the app.init() method, we add an event handler that reads the current slider position and updates the Calendar zoom level using the zoom.setActive() method.
JavaScript:
app.elements.zoomLevel.addEventListener("input", (ev) => {
const level = parseInt(ev.target.value, 10);
app.applyLevel(level);
});Zoom Buttons

In this step, we will add [-] and [+] buttons to our application. These buttons allow users to zoom in and out incrementally.
HTML:
<div class="calendar-toolbar">
<span>Magnification:</span>
<button id="minus" type="button" aria-label="Decrease magnification">-</button>
<input type="range" min="0" max="2" step="1" id="zoomLevel" value="0" aria-label="Magnification level"/>
<button id="plus" type="button" aria-label="Increase magnification">+</button>
<span id="label"></span>
</div>To activate the zoom buttons, we add click event handlers in app.init(). The button handlers update the range input and then call the same applyLevel() method used by the slider.
app.elements.buttonMinus.addEventListener("click", () => {
const currentValue = parseInt(app.elements.zoomLevel.value, 10);
if (currentValue > 0) {
const newValue = currentValue - 1;
app.applyLevel(newValue);
}
});
app.elements.buttonPlus.addEventListener("click", () => {
const currentValue = parseInt(app.elements.zoomLevel.value, 10);
if (currentValue < dp.zoomLevels.length - 1) {
const newValue = currentValue + 1;
app.applyLevel(newValue);
}
});Displaying the Selected Zoom Level Name
The applyLevel() function updates the text label, applies the CSS class for font scaling, and switches the Calendar zoom level.
applyLevel(level) {
const zoomClasses = dp.zoomLevels.map((item) => item.cssClass);
const zoomClass = dp.zoomLevels[level].cssClass;
app.elements.calendar.classList.remove(...zoomClasses);
app.elements.calendar.classList.add(zoomClass);
app.elements.zoomLevel.value = level;
app.elements.label.innerText = dp.zoomLevels[level].name;
dp.zoom.setActive(level);
}Complete Source Code
This is the complete src/app.js source code of a JavaScript Calendar example that lets you magnify the Calendar using an HTML slider and zoom buttons:
import {DayPilot} from "@daypilot/daypilot-lite-javascript";
const dp = new DayPilot.Calendar("dp", {
eventClickHandling: "Disabled",
eventHoverHandling: "Disabled",
timeRangeSelectedHandling: "Enabled",
durationBarVisible: false,
onTimeRangeSelected: async (args) => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
const calendar = args.control;
calendar.clearSelection();
if (modal.canceled) { return; }
calendar.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
text: modal.result
});
},
eventMoveHandling: "Update",
onEventMoved: (args) => {
args.control.message("Event moved: " + args.e.text());
},
eventResizeHandling: "Update",
onEventResized: (args) => {
args.control.message("Event resized: " + args.e.text());
},
viewType: "Week",
heightSpec: "BusinessHoursNoScroll",
zoomLevels: [
{
name: "100%",
cssClass: "zoom100",
properties: {
cellHeight: 20,
headerHeight: 30
}
},
{
name: "115%",
cssClass: "zoom115",
properties: {
cellHeight: 23,
headerHeight: 35
}
},
{
name: "130%",
cssClass: "zoom130",
properties: {
cellHeight: 26,
headerHeight: 40
}
}
]
});
dp.init();
const app = {
elements: {
calendar: document.querySelector("#dp"),
zoomLevel: document.querySelector("#zoomLevel"),
buttonMinus: document.querySelector("#minus"),
buttonPlus: document.querySelector("#plus"),
label: document.querySelector("#label")
},
init() {
const events = [
{
id: "1",
start: DayPilot.Date.today().addHours(10),
end: DayPilot.Date.today().addHours(12),
text: "Event 1"
}
];
dp.update({events});
app.elements.zoomLevel.addEventListener("input", (ev) => {
const level = parseInt(ev.target.value, 10);
app.applyLevel(level);
});
app.elements.buttonMinus.addEventListener("click", () => {
const currentValue = parseInt(app.elements.zoomLevel.value, 10);
if (currentValue > 0) {
const newValue = currentValue - 1;
app.applyLevel(newValue);
}
});
app.elements.buttonPlus.addEventListener("click", () => {
const currentValue = parseInt(app.elements.zoomLevel.value, 10);
if (currentValue < dp.zoomLevels.length - 1) {
const newValue = currentValue + 1;
app.applyLevel(newValue);
}
});
app.applyLevel(0);
},
applyLevel(level) {
const zoomClasses = dp.zoomLevels.map((item) => item.cssClass);
const zoomClass = dp.zoomLevels[level].cssClass;
app.elements.calendar.classList.remove(...zoomClasses);
app.elements.calendar.classList.add(zoomClass);
app.elements.zoomLevel.value = level;
app.elements.label.innerText = dp.zoomLevels[level].name;
dp.zoom.setActive(level);
}
};
app.init();
DayPilot




