Overview

Since version 2023.1.5547, the JavaScript calendar component includes a new zoom feature.

The main purpose of the calendar's 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 - magnification the calendar elements for better visibility.

  • Three zoom levels are defined, each with a different magnification of the calendar elements and text size.

  • Users can switch a zoom level using a slider displayed above the calendar.

  • It is also possible to change the magnification level using [-] and [+] buttons.

The project includes a trial version of DayPilot Pro for JavaScript (see License below).

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

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 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's also possible to replace them with a function that returns the desired value (and calculates it dynamically).

  • The name property of each zoom level is not part of the official API (you can include any custom properties at this level). We add the name property to provide a text description that can be displayed in the application when describing the current zoom level.

The cellHeight value also indirectly affects the event box dimensions.

Adjusting the Calendar font size

To adjust the font size of all elements, it is necessary to apply a special CSS class at the top level using the cssClass property and define custom font sizes of elements:

const dp = new DayPilot.Calendar("dp", {
  // ...
  zoomLevels: [
    {
      name: "100%",
      properties: {
        // ...
        cssClass: "zoom100"
      },

    },
    {
      name: "115%",
      properties:
        {
        // ...
          cssClass: "zoom115"
        }
    },
    {
      name: "130%",
      properties :{
        // ...
        cssClass: "zoom130"
      }
    },
  ]
});

The zoomX classes are used to define the font size for Calendar elements:

<style>
  .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 div {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;}
</style>

Slider Control

javascript calendar zoom magnification slider

The slider control is defined using the standard <input type="range"> HTML element.

HTML:

<div class="calendar-toolbar">
  Magnification:
  <input type="range" min="0" max="2" step="1" id="zoomLevel" value="0"/>
</div>

In the app.init() method, we add an event handler that reads the current slider position and updates the calendar zoom level using zoom.setActive() method.

JavaScript:

const app = {
  elements: {
    zoomLevel: document.querySelector("#zoomLevel"),
    label: document.querySelector("#label")
  },
  init() {
    app.elements.zoomLevel.addEventListener("input", function(ev) {
      var level = parseInt(this.value);
      app.applyLevel(level);
    });

    this.applyLevel(0);
  },
  applyLevel(level) {
    app.elements.label.innerText = dp.zoomLevels[level].name;
    dp.zoom.setActive(level);
  }
};

Zoom buttons

javascript calendar zoom magnification 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">
  Magnification:
  &nbsp;
  <button id="minus">-</button>
  <input type="range" min="0" max="2" step="1" id="zoomLevel" value="0"/>
  <button id="plus">+</button>
  &nbsp;
  <span id="label"></span>
</div>

To activate the zoom buttons, we add “click” event handlers in app.init():

const app = {
  elements: {
    zoomLevel: document.querySelector("#zoomLevel"),
    buttonMinus: document.querySelector("#minus"),
    buttonPlus: document.querySelector("#plus"),
    label: document.querySelector("#label")
  },
  init() {

    // ...

    app.elements.buttonMinus.addEventListener("click", function () {
      const currentValue = parseInt(app.elements.zoomLevel.value);
      if (currentValue > 0) {
        const newValue = currentValue - 1;
        app.elements.zoomLevel.value = newValue;
        dp.zoom.setActive(newValue);
      }
    });

    app.elements.buttonPlus.addEventListener("click", function () {
      const currentValue = parseInt(app.elements.zoomLevel.value);
      if (currentValue < dp.zoomLevels.length - 1) {
        const newValue = currentValue + 1;
        app.elements.zoomLevel.value = newValue;
        dp.zoom.setActive(newValue);
      }
    });

    this.applyLevel(0);
  },
  applyLevel(level) {
    app.elements.label.innerText = dp.zoomLevels[level].name;
    dp.zoom.setActive(level);
  }
};

Displaying the selected zoom level name

The applyLevel() function switches the Calendar zoom level and updates the label text to display the name of the current zoom level.

applyLevel(level) {
  app.elements.label.innerText = dp.zoomLevels[level].name;
  dp.zoom.setActive(level);
}

Complete source code

This is the complete source code of a JavaScript Calendar component example that lets you magnify the calendar using a slider HTML control:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JavaScript Calendar: Zoom</title>

  <style type="text/css">
    p, body, td, input, select, button { font-family: -apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif; font-size: 14px; }
    body { padding: 0px; margin: 0px; background-color: #ffffff; }
    a { color: #1155a3; }
    .space { margin: 10px 0px 10px 0px; }
    .header { background: #003267; background: linear-gradient(to right, #011329 0%,#00639e 44%,#011329 100%); padding:20px 10px; color: white; box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.75); }
    .header a { color: white; }
    .header h1 a { text-decoration: none; }
    .header h1 { padding: 0px; margin: 0px; }
    .main { padding: 10px; margin-top: 10px; }
    .generated { color: #999; margin-top: 10px; }
    .generated a { color: #999; }
  </style>

  <style>
    .calendar-toolbar {
      margin: 10px 0px;
      display: flex;
      align-items: center;
    }

    .calendar-toolbar button {
      width: 25px;
      height: 25px;

      background-color: #3c78d8;
      color: white;
      border: 0;
      cursor: pointer;
    }
    .controls button:focus {
      outline: none;
    }
  </style>

  <style>
    body .calendar_default_event_inner {
      background: #93c47d;
      color: #ffffff;
      border-radius: 10px;
      border-color: #367c5a;
    }
  </style>

  <style>
    .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 div {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;}
  </style>


  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>

</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/35160/javascript-calendar-zoom'>JavaScript Calendar: Zoom</a></h1>
  <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React/Vue</div>
</div>

<div class="main">
  <div class="calendar-toolbar">
    Magnification:
    &nbsp;
    <button id="minus">-</button>
    <input type="range" min="0" max="2" step="1" id="zoomLevel" value="0"/>
    <button id="plus">+</button>
    &nbsp;
    <span id="label"></span>
  </div>

  <div id="dp"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>

<script>
  const dp = new DayPilot.Calendar("dp", {
    viewType: "Week",
    cellHeight: 30,
    timeRangeSelectedHandling: "Enabled",
    durationBarVisible: false,
    onTimeRangeSelected: async (args) => {
      const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
      const dp = args.control;
      dp.clearSelection();
      if (modal.canceled) { return; }
      dp.events.add({
        start: args.start,
        end: args.end,
        id: DayPilot.guid(),
        text: modal.result
      });
    },
    eventDeleteHandling: "Disabled",
    eventMoveHandling: "Update",
    onEventMoved: (args) => {
      args.control.message("Event moved: " + args.e.text());
    },
    eventResizeHandling: "Update",
    onEventResized: (args) => {
      args.control.message("Event resized: " + args.e.text());
    },
    eventClickHandling: "Disabled",
    eventHoverHandling: "Disabled",
    zoomLevels: [
      {
        name: "100%",
        properties: {
          cellHeight: 20,
          headerHeight: 30,
          cssClass: "zoom100"
        },

      },
      {
        name: "115%",
        properties:
          {
            cellHeight: 23,
            headerHeight: 35,
            cssClass: "zoom115"
          }
      },
      {
        name: "130%",
        properties :{
          cellHeight: 26,
          headerHeight: 40,
          cssClass: "zoom130"
        }
      },
    ]
  });
  dp.events.list = [
    {
      id: "1",
      start: DayPilot.Date.today().addHours(10),
      end: DayPilot.Date.today().addHours(12),
      text: "Event 1"
    }
  ];
  dp.init();

  const app = {
    elements: {
      zoomLevel: document.querySelector("#zoomLevel"),
      buttonMinus: document.querySelector("#minus"),
      buttonPlus: document.querySelector("#plus"),
      label: document.querySelector("#label")
    },
    init() {
      app.elements.zoomLevel.addEventListener("input", function(ev) {
        var level = parseInt(this.value);
        app.applyLevel(level);
      });

      app.elements.buttonMinus.addEventListener("click", function () {
        const currentValue = parseInt(app.elements.zoomLevel.value);
        if (currentValue > 0) {
          const newValue = currentValue - 1;
          app.elements.zoomLevel.value = newValue;
          app.applyLevel(newValue);
        }
      });

      app.elements.buttonPlus.addEventListener("click", function () {
        const currentValue = parseInt(app.elements.zoomLevel.value);
        if (currentValue < dp.zoomLevels.length - 1) {
          const newValue = currentValue + 1;
          app.elements.zoomLevel.value = newValue;
          app.applyLevel(newValue);
        }
      });

      this.applyLevel(0);
    },
    applyLevel(level) {
      app.elements.label.innerText = dp.zoomLevels[level].name;
      dp.zoom.setActive(level);
    }
  };
  app.init();

</script>

</body>
</html>