---
title: "Javascript Timeline Calendar Zoom Levels Example | Mobiscroll"
component: "Timeline"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/timeline/calendar-zoom"
---

# Calendar Zoom Levels - JavaScript Timeline demo

Add zoom controls to a Javascript resource timeline to switch from day/hour detail to monthly/yearly overviews— keeping events centered as the view changes. Plain JavaScript API.

- [Live demo](https://demo.mobiscroll.com/javascript/timeline/calendar-zoom)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-javascript/tree/main/src/demos/eventcalendar/timeline/calendar-zoom)
- This demo in other frameworks: [jQuery](https://demo.mobiscroll.com/jquery/timeline/calendar-zoom.md), [Angular](https://demo.mobiscroll.com/angular/timeline/calendar-zoom.md), [React](https://demo.mobiscroll.com/react/timeline/calendar-zoom.md), [Vue](https://demo.mobiscroll.com/vue/timeline/calendar-zoom.md)

## Description

The timeline provides a continuous rendering of events across short and long periods of time. To simplify working with many different types of events that are rendered through various stretches of time, being able to zoom in and out is essential.

Through the [`zoomLevel`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-zoomLevel) option it is possible to switch between different predefined levels of granularity. Depending on what you are trying to accomplish, any number of levels can be specified in the [`zoomLevels`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#view-timeline-zoomLevels) property, controlling settings ranging from horizontal resolution, page type and size to column widths.

Combined with the flexibility of [templating capabilities of the event calendar header](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHeader) , you can add buttons, sliders to the calendar or manage everything from external controls.

Dynamically zooming in and out will always keep a virtual reference point in the middle of the timeline for a smooth visual experience.

## What this demo shows

- A desktop timeline that lets users change the visible time span with dynamic zoom levels while keeping dates horizontal and resources listed vertically.
- **Header navigation** The timeline header includes date navigation, Previous, Today, and Next controls, plus zoom controls for increasing or decreasing the timeline scale.
- **Timeline header** The header labels adjust to the active zoom level, showing appropriate day, week, or month groupings for the visible range.
- **Date labels** The visible days use the `DD DDD` date format, with the current date highlighted.
- **Zoom behavior** The timeline changes its date resolution as the zoom level changes, ranging from broad year and month views to finer day and hour-level views.
- **Centered zooming** Zooming keeps the current view anchored around the visible reference date so users can move between overview and detail without losing context.
- **Resources** Multiple resources are displayed as timeline rows, each with its own scheduled events.
- **Event scaling** Events are repositioned and resized as the zoom level changes while preserving their start and end dates.
- **Multi-day events** Events can span consecutive days or weeks and remain aligned to the timeline scale.
- **Event cards** Events appear as colored cards with a left color stripe, event name in bold, and exact start and end time.
- **Event positioning** Events are placed by assigned resource, date, start time, and end time.
- **Event interaction** Events highlight on hover and expose drag and resize handles for moving events or changing their duration.
- **Event selection** Clicking an event selects and highlights it.
- **Horizontal scrolling** The timeline supports horizontal scrolling for navigating the visible date and time range at any zoom level.
- **Vertical scrolling** The resource area scrolls vertically so many resources can be shown in the same timeline.

## Best for

- **Planning across different time scales** Switching between detailed scheduling and higher-level planning views without changing the underlying event data.
- **Resource timelines** Showing multiple resources in rows while keeping scheduled work aligned to dates and times.
- **Long-range schedules** Navigating large date ranges where users need both broad overviews and closer inspection of individual events.
- **Multi-day work** Displaying events that span several days or weeks while keeping their duration visible at different zoom levels.
- **Custom timeline headers** Combining standard calendar navigation with custom controls such as zoom buttons and a zoom range input.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-themeVariant) - option
- [`clickToCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-eventDelete) - option
- [`resources`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-resources) - option
- [`refDate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-refDate) - option
- [`zoomLevel`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-zoomLevel) - option
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`renderHeader`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHeader) - renderer
- [`setOptions`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setOptions) - method
- [`setEvents`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setEvents) - method

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

var zoomLevel = 9;

var myResources = [
  { id: 1, name: 'Resource A', color: '#e20000' },
  { id: 2, name: 'Resource B', color: '#76e083' },
  { id: 3, name: 'Resource C', color: '#4981d6' },
  { id: 4, name: 'Resource D', color: '#e25dd2' },
  { id: 5, name: 'Resource E', color: '#1dab2f' },
  { id: 6, name: 'Resource F', color: '#d6d145' },
];

var myCalendar = mobiscroll.eventcalendar('#demo-calendar-zoom', {
  clickToCreate: true,
  dragToCreate: true,
  dragToMove: true,
  dragToResize: true,
  eventDelete: true,
  resources: myResources,
  refDate: getRefDate(new Date()),
  zoomLevel: zoomLevel,
  view: {
    timeline: {
      zoomLevels: {
        1: { type: 'year', size: 25, resolutionHorizontal: 'year', columnWidth: 'small' },
        2: { type: 'year', size: 25, resolutionHorizontal: 'year', columnWidth: 'xlarge' },
        3: { type: 'year', size: 25, resolutionHorizontal: 'quarter', columnWidth: 'small' },
        4: { type: 'year', size: 25, resolutionHorizontal: 'quarter', columnWidth: 'xlarge' },
        5: { type: 'year', size: 25, resolutionHorizontal: 'month', columnWidth: 'medium' },
        6: { type: 'year', size: 25, resolutionHorizontal: 'month', columnWidth: 'xxxlarge' },
        7: { type: 'year', size: 25, resolutionHorizontal: 'week', columnWidth: 'medium' },
        8: { type: 'year', size: 25, resolutionHorizontal: 'week', columnWidth: 'xxxlarge' },
        9: { type: 'year', size: 25, resolutionHorizontal: 'day', columnWidth: 'small' },
        10: { type: 'year', size: 25, resolutionHorizontal: 'day', columnWidth: 'xlarge' },
        11: { type: 'year', size: 3, resolutionHorizontal: 'hour', columnWidth: 'xlarge', timeCellStep: 720, timeLabelStep: 720 },
        12: { type: 'year', size: 3, resolutionHorizontal: 'hour', columnWidth: 'xlarge', timeCellStep: 360, timeLabelStep: 360 },
        13: { type: 'year', size: 3, resolutionHorizontal: 'hour', columnWidth: 'xlarge', timeCellStep: 180, timeLabelStep: 180 },
        14: { type: 'year', size: 3, resolutionHorizontal: 'hour', columnWidth: 'medium', timeCellStep: 60, timeLabelStep: 60 },
        15: { type: 'month', size: 3, resolutionHorizontal: 'hour', timeCellStep: 30, timeLabelStep: 30, columnWidth: 'medium' },
        16: { type: 'month', size: 3, resolutionHorizontal: 'hour', timeCellStep: 30, timeLabelStep: 30, columnWidth: 'xxxlarge' },
        17: { type: 'month', size: 3, resolutionHorizontal: 'hour', timeCellStep: 15, timeLabelStep: 15, columnWidth: 'xxxlarge' },
        18: { type: 'month', size: 3, resolutionHorizontal: 'hour', timeCellStep: 5, timeLabelStep: 5, columnWidth: 'large' },
      },
    },
  },
  renderHeader: function () {
    return (
      '<div mbsc-calendar-nav></div>' +
      '<div class="mbsc-flex mbsc-flex-1-0 mbsc-justify-content-end">' +
      '<button id="demo-zoom-level-out" class="mds-button-zoom-level" mbsc-button data-icon="minus" data-variant="flat"></button>' +
      '<input id="demo-zoom-level-slider" type="range" min="1" max="18" value="9" />' +
      '<button id="demo-zoom-level-in" class="mds-button-zoom-level" mbsc-button data-icon="plus" data-variant="flat"></button>' +
      '</div>' +
      '<div mbsc-calendar-prev></div>' +
      '<div mbsc-calendar-today></div>' +
      '<div mbsc-calendar-next></div>' +
      '</div>'
    );
  },
});

function getRefDate(viewDate) {
  if (zoomLevel < 11) {
    return new Date(viewDate.getFullYear() - 12, 0, 1);
  }
  if (zoomLevel < 15) {
    return new Date(viewDate.getFullYear() - 1, 0, 1);
  }
  return new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1);
}

function handleZoom(zoom) {
  zoomLevel = zoom;

  document.getElementById('demo-zoom-level-slider').value = zoomLevel;
  document.getElementById('demo-zoom-level-in').disabled = zoomLevel === 18;
  document.getElementById('demo-zoom-level-out').disabled = zoomLevel === 1;

  var viewDate = myCalendar ? myCalendar.getViewDate() : new Date();

  myCalendar.setOptions({
    refDate: getRefDate(viewDate),
    zoomLevel: zoomLevel,
  });
}

document.getElementById('demo-zoom-level-slider').addEventListener('input', function (ev) {
  handleZoom(+ev.target.value);
});

document.getElementById('demo-zoom-level-in').addEventListener('click', function () {
  handleZoom(zoomLevel + 1);
});

document.getElementById('demo-zoom-level-out').addEventListener('click', function () {
  handleZoom(zoomLevel - 1);
});

mobiscroll.getJson(
  'https://trial.mobiscroll.com/timeline-zoom-events/',
  function (events) {
    myCalendar.setEvents(events);
  },
  'jsonp',
);
```

### HTML

```html
<div id="demo-calendar-zoom"></div>
```

## Implementation notes

- Define a `zoomLevels` map inside the `timeline` view config: integer keys (1-18) each map to an object with `type`, `size`, `resolutionHorizontal`, `columnWidth`, and optionally `timeCellStep`/`timeLabelStep`. The active key is controlled by the `zoomLevel` prop on `Eventcalendar`. Levels should progress from coarsest (yearly columns, small width) to finest (sub-hour columns).
- The demo uses three resolution bands across 18 levels: levels 1-10 keep `type: 'year', size: 25` and vary `resolutionHorizontal` through `'year'`, `'quarter'`, `'month'`, `'week'`, `'day'` with alternating narrow/wide `columnWidth`; levels 11-14 switch to `type: 'year', size: 3` with `resolutionHorizontal: 'hour'` and `timeCellStep`/`timeLabelStep` stepping from 720 down to 60 min; levels 15-18 use `type: 'month', size: 3` with 30-, 15-, and 5-minute steps.
- Track the active zoom as a state variable (default: 9). Pass it to the `zoomLevel` prop. Provide zoom-in and zoom-out callbacks that increment/decrement the state by 1; clamp to the defined range.
- Adjust `refDate` whenever `zoomLevel` changes so the timeline stays anchored around the currently visible date. Read the current view date from the calendar instance via `calRef.current.getViewDate()`. For levels 1-10 (25-year span) offset 12 years back; for levels 11-14 (3-year span) offset 1 year back; for levels 15-18 (3-month span) offset 1 month back. Pass the computed date as the `refDate` prop.
- Use `renderHeader` (Angular: `headerTemplate`, Vue: `#header` slot) to render: `CalendarNav` on the left; a zoom control group on the right containing a flat minus `Button` (disabled at level 1), a native `<input type="range" min="1" max="18">` bound to the zoom state, and a flat plus `Button` (disabled at the maximum level); then the standard `CalendarPrev`, `CalendarToday`, `CalendarNext` controls.
- Define 6 resources with `id`, `name`, and `color`. Load events from a remote endpoint using `getJson` and pass them to `data`.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-javascript.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config javascript
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config javascript --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/javascript/getting-started/installation

## Related demos

- Previous: [Multiple Time Scales](https://demo.mobiscroll.com/javascript/timeline/multiple-days-weeks-months-quarters-years-variable-resolution.md) - Configure span and time scale on a Javascript Timeline — switch day, week, month, quarter, or year views, set resolution, and show or hide empty rows.
- Next: [Limit Concurrent Events](https://demo.mobiscroll.com/javascript/timeline/control-number-of-concurrently-shown-events.md) - Set a maximum number of concurrent events on a Javascript Timeline — cap overlapping events per row and show an overflow indicator for hidden events.
