---
title: "Javascript Event calendar Custom Event Tooltip Example | Mobiscroll"
component: "Event calendar"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/eventcalendar/custom-event-tooltip"
---

# Custom Event Tooltip - JavaScript Event calendar demo

Add custom hover tooltips to a Javascript event calendar — show appointment details, status, location, and confirm or delete actions to each event. Plain JS api for usage everywhere.

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

## Description

There are several approaches to showing a tooltip when hovering events.

**The native tooltip**

By default, the calendar shows the browser native tooltip when hovering over the event. This includes the times and title of the event, which does the job most of the times. For showing a custom text use the [`tooltip`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-data) property of the data object. This tooltip is specific to every event. If you want to hide the native tooltip, you can set the [`showEventTooltip`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-showEventTooltip) to `false`.

**Fully custom tooltip**

Setting the [`showEventTooltip`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-showEventTooltip) to false gives room for a fully custom tooltip that can be implemented by using the [`onEventHoverIn`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventHoverIn) and [`onEventHoverOut`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventHoverOut) lifecycle events. With the help of the Mobiscroll popup you can show a custom tooltip that holds details, actions applicable to the event it is anchored to.

**Showing a third party popup**

There are cases when you are using a third party library, something like md-boostrap. For those cases, turn the browser native tooltip off with the [`showEventTooltip`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-showEventTooltip) option and use the third-party tooltip accordingly. In case of md-boostrap, you can add the tooltip directive to the [custom event template](https://demo.mobiscroll.com/javascript/eventcalendar/customize-label-look-and-feel) so that the library knows where and when to show the tooltip.

## What this demo shows

- A desktop-style weekly event calendar view with event labels rendered inside day cells.
- **Event labels** Events are shown as labels, with different visual styles based on the event type or event data.
- **Event selection** Clicking an event label highlights the selected event.
- **Day hover state** Hovering over a day cell highlights the day number with a gray background.
- **Day selection** Clicking an empty area of a day cell selects that day and highlights the day number with a blue background.
- **Calendar header** The top-left side shows the current month and year, while the top-right side includes blue previous and next navigation arrows with a `Today` button between them.
- **Drag navigation** The calendar can be dragged left or right to navigate between weeks.
- **Custom event tooltip** Hovering over an event opens a custom tooltip below the hovered event.
- **Tooltip header** The tooltip header uses the event color and shows the patient name, age, and event start and end time.
- **Tooltip details** The tooltip body shows `Status`, `Reason for visiting`, and `Location` rows.
- **Appointment status** The `Status` row shows whether the appointment is confirmed.
- **Status action** The tooltip shows either a `Confirm appointment` or `Cancel appointment` button based on the current appointment status.
- **Status feedback** Clicking the status action changes the appointment status, closes the tooltip, and shows a toast message on the calendar.
- **Reason for visiting** The tooltip includes a short description of why the patient is visiting.
- **Location** The tooltip shows the appointment location.
- **Footer actions** The footer includes a gray `View patient file` button and a red `Delete appointment` button.
- **View patient action** Clicking `View patient file` shows a toast message with the `View file` text.
- **Delete action** Clicking `Delete appointment` removes the event.

## Best for

- **Appointment calendars** Showing patient appointments with status, reason, location, and event-specific actions.
- **Healthcare scheduling** Building calendar views where users need quick access to appointment details without opening a separate page.
- **Desktop workflows** Presenting a weekly calendar layout with hover states, selection states, and header navigation.
- **Event detail previews** Displaying structured event information in a custom tooltip anchored to the hovered event.
- **Inline appointment actions** Letting users confirm, cancel, view, or delete appointments directly from the calendar.

## 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
- [`setOptions`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setOptions) - method
- [`setOptions`](https://mobiscroll.com/docs/javascript/popup/api#method-setOptions) - method
- [`anchor`](https://mobiscroll.com/docs/javascript/popup/api#opt-anchor) - option
- [`open`](https://mobiscroll.com/docs/javascript/popup/api#method-open) - method
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-data) - 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
- [`height`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-height) - option
- [`showEventTooltip`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-showEventTooltip) - option
- [`onEventDragStart`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventDragStart) - event
- [`close`](https://mobiscroll.com/docs/javascript/popup/api#method-close) - method
- [`onEventClick`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventClick) - event
- [`onEventHoverIn`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventHoverIn) - event
- [`onEventHoverOut`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventHoverOut) - event
- [`display`](https://mobiscroll.com/docs/javascript/popup/api#opt-display) - option
- [`scrollLock`](https://mobiscroll.com/docs/javascript/popup/api#opt-scrollLock) - option
- [`showOverlay`](https://mobiscroll.com/docs/javascript/popup/api#opt-showOverlay) - option
- [`touchUi`](https://mobiscroll.com/docs/javascript/popup/api#opt-touchUi) - option
- [`width`](https://mobiscroll.com/docs/javascript/popup/api#opt-width) - option
- [`updateEvent`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-updateEvent) - method
- [`removeEvent`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-removeEvent) - method

## Code

### JS

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

function openTooltip(args) {
  var formatDate = mobiscroll.formatDate;
  var event = args.event;
  var time = formatDate('hh:mm A', new Date(event.start)) + ' - ' + formatDate('hh:mm A', new Date(event.end));
  var buttonText;
  var buttonType;

  if (event.confirmed) {
    buttonText = 'Cancel appointment';
    buttonType = 'warning';
  } else {
    buttonText = 'Confirm appointment';
    buttonType = 'success';
  }

  appointment = event;
  appointmentInfo.innerHTML = event.title + ', Age: ' + event.age;
  appointmentLocation.innerHTML = event.location;
  appointmentReason.innerHTML = event.reason;
  appointmentTime.innerHTML = time;
  appointmentStatus.innerHTML = event.confirmed ? 'Confirmed' : 'Canceled';

  btnStatus.querySelector('.mbsc-button-txt').innerHTML = buttonText;
  mobiscroll.getInst(btnStatus).setOptions({ color: buttonType });

  tooltipHeader.style.backgroundColor = event.color;

  clearTimeout(timer);
  timer = null;

  tooltip.setOptions({ anchor: args.domEvent.target });
  tooltip.open();
}

var appointment;
var appointmentInfo = document.getElementById('demo-tooltip-info');
var appointmentLocation = document.getElementById('demo-tooltip-location');
var appointmentReason = document.getElementById('demo-tooltip-reason');
var appointmentStatus = document.getElementById('demo-tooltip-status');
var appointmentTime = document.getElementById('demo-tooltip-time');
var btnDelete = document.getElementById('demo-tooltip-delete');
var btnStatus = document.getElementById('demo-tooltip-status-update');
var btnView = document.getElementById('demo-tooltip-view');
var timer;
var tooltipElm = document.getElementById('demo-tooltip-popup');
var tooltipHeader = document.getElementById('demo-tooltip-header');

var calendar = mobiscroll.eventcalendar('#demo-tooltip-calendar', {
  view: { calendar: { type: 'week' } },
  data: [
    {
      title: 'Jude Chester',
      age: 69,
      start: '2026-10-21T08:00',
      end: '2026-10-21T09:00',
      confirmed: false,
      reason: 'Headaches morning & afternoon',
      location: 'Topmed, Building A, Room 203',
      color: '#b33d3d',
    },
    {
      title: 'Leon Porter',
      age: 44,
      start: '2026-10-21T09:00',
      end: '2026-10-21T10:00',
      confirmed: false,
      reason: 'Left abdominal pain',
      location: 'Topmed, Building D, Room 360',
      color: '#b33d3d',
    },
    {
      title: 'Lily Racquel',
      age: 54,
      start: '2026-10-21T10:00',
      end: '2026-10-21T11:00',
      confirmed: true,
      reason: 'Dry, persistent cough & headache',
      location: 'Procare, Building C, Room 12',
      color: '#309346',
    },
    {
      title: 'Mia Sawyer',
      age: 59,
      start: '2026-10-21T11:00',
      end: '2026-10-21T12:00',
      confirmed: true,
      reason: 'Difficulty sleeping & loss of appetite',
      location: 'Procare, Building C, Room 12',
      color: '#309346',
    },
    {
      title: 'Jon Candace',
      age: 63,
      start: '2026-10-21T12:00',
      end: '2026-10-21T13:00',
      confirmed: true,
      reason: 'Nausea & weakness',
      location: 'MedStar, Building A, Room 1',
      color: '#c77c0a',
    },
    {
      title: 'Layton Drake',
      age: 57,
      start: '2026-10-21T13:00',
      end: '2026-10-21T14:00',
      confirmed: true,
      reason: 'Headaches & loss of appetite',
      location: 'Vitalife, Room 160',
      color: '#c77c0a',
    },
    {
      title: 'Willis Kane',
      age: 44,
      start: '2026-10-22T08:00',
      end: '2026-10-22T09:00',
      confirmed: true,
      reason: 'Back pain',
      location: 'Care Cente, Room 320r',
      color: '#b33d3d',
    },
    {
      title: 'Theo Calanthia',
      age: 60,
      start: '2026-10-22T09:00',
      end: '2026-10-22T10:00',
      confirmed: true,
      reason: 'Anxiousness & sleeping disorder',
      location: 'Care Center, Room 320',
      color: '#b33d3d',
    },
    {
      title: 'Ford Kaiden',
      age: 53,
      start: '2026-10-22T14:00',
      end: '2026-10-22T15:00',
      confirmed: true,
      reason: 'Nausea & vomiting',
      location: 'Care Center, Room 206',
      color: '#b33d3d',
    },
    {
      title: 'Gerry Irma',
      age: 50,
      start: '2026-10-22T13:00',
      end: '2026-10-22T14:00',
      confirmed: false,
      reason: 'Fever & sore throat',
      location: 'Medica Zone, Building C, Room 2',
      color: '#c77c0a',
    },
    {
      title: 'Carlyn Dorothy',
      age: 36,
      start: '2026-10-22T14:00',
      end: '2026-10-22T15:00',
      confirmed: true,
      reason: 'Tiredness & muscle pain',
      location: 'Medica Zone, Building C, Room 2',
      color: '#c77c0a',
    },
    {
      title: 'Alma Potter',
      age: 74,
      start: '2026-10-20T10:00',
      end: '2026-10-20T11:00',
      confirmed: true,
      reason: 'High blood pressure',
      location: 'Vitacure, Building D, Room 2',
      color: '#b33d3d',
    },
    {
      title: 'Debra Aguilar',
      age: 47,
      start: '2026-10-20T11:00',
      end: '2026-10-20T12:00',
      confirmed: false,
      reason: 'Fever & sore throat',
      location: 'Vitacure, Building D, Room 2',
      color: '#b33d3d',
    },
    {
      title: 'Marjorie White',
      age: 55,
      start: '2026-10-20T13:00',
      end: '2026-10-20T14:00',
      confirmed: true,
      reason: 'Back pain',
      location: 'Vitacure, Building D, Room 2',
      color: '#b33d3d',
    },
    {
      title: 'Lora Wilson',
      age: 66,
      start: '2026-10-20T15:00',
      end: '2026-10-20T16:00',
      confirmed: false,
      reason: 'Fever & headache',
      location: 'Vitacure, Building D, Room 2',
      color: '#b33d3d',
    },
    {
      title: 'Christie Baker',
      age: 71,
      start: '2026-10-20T10:00',
      end: '2026-10-20T11:00',
      confirmed: true,
      reason: 'Headaches morning & afternoon',
      location: 'Care Center, Room 300',
      color: '#309346',
    },
    {
      title: 'Arlene Lyons',
      age: 41,
      start: '2026-10-20T14:00',
      end: '2026-10-20T15:00',
      confirmed: true,
      reason: 'Nausea & weakness',
      location: 'Care Center, Room 202',
      color: '#c77c0a',
    },
    {
      title: 'Dory Edie',
      age: 45,
      start: '2026-10-19T09:00',
      end: '2026-10-19T10:00',
      confirmed: true,
      reason: 'Right abdominal pain',
      location: 'Vitacure, Building A, Room 203',
      color: '#309346',
    },
    {
      title: 'Kaylin Toni',
      age: 68,
      start: '2026-10-19T10:00',
      end: '2026-10-19T11:00',
      confirmed: true,
      reason: 'Itchy, red rashes',
      location: 'Vitacure, Building A, Room 203',
      color: '#309346',
    },
    {
      title: 'Gray Kestrel',
      age: 60,
      start: '2026-10-19T12:00',
      end: '2026-10-19T13:00',
      confirmed: true,
      reason: 'Cough & fever',
      location: 'Vitacure, Building A, Room 203',
      color: '#309346',
    },
    {
      title: 'Lou Andie',
      age: 76,
      start: '2026-10-19T15:00',
      end: '2026-10-19T16:00',
      confirmed: true,
      reason: 'High blood pressure',
      location: 'Medica Zone, Room 13',
      color: '#309346',
    },
    {
      title: 'Yancy Dustin',
      age: 52,
      start: '2026-10-19T10:00',
      end: '2026-10-19T11:00',
      confirmed: true,
      reason: 'Fever & headache',
      location: 'Vitacure, Building E, Room 50',
      color: '#c77c0a',
    },
    {
      title: 'Terry Clark',
      age: 78,
      start: '2026-10-19T11:00',
      end: '2026-10-19T12:00',
      confirmed: true,
      reason: 'Swollen ankles',
      location: 'Vitacure, Building E, Room 50',
      color: '#c77c0a',
    },
  ],
  clickToCreate: false,
  dragToCreate: false,
  dragToMove: true,
  dragToResize: false,
  height: 260,
  showEventTooltip: false,
  onEventDragStart: function () {
    tooltip.close();
  },
  onEventClick: openTooltip,
  onEventHoverIn: openTooltip,
  onEventHoverOut: function () {
    if (!timer) {
      timer = setTimeout(function () {
        tooltip.close();
      }, 200);
    }
  },
});

var tooltip = mobiscroll.popup('#demo-tooltip-popup', {
  contentPadding: false,
  display: 'anchored',
  scrollLock: false,
  showOverlay: false,
  touchUi: false,
  width: 350,
});

tooltipElm.addEventListener('mouseenter', function () {
  if (timer) {
    clearTimeout(timer);
    timer = null;
  }
});

tooltipElm.addEventListener('mouseleave', function () {
  timer = setTimeout(function () {
    tooltip.close();
  }, 200);
});

btnStatus.addEventListener('click', function () {
  appointment.confirmed = !appointment.confirmed;
  calendar.updateEvent(appointment);
  tooltip.close();
  mobiscroll.toast({
    message: 'Appointment ' + (appointment.confirmed ? 'confirmed' : 'canceled'),
  });
});

btnView.addEventListener('click', function () {
  tooltip.close();
  mobiscroll.toast({
    message: 'View file',
  });
});

btnDelete.addEventListener('click', function () {
  calendar.removeEvent(appointment);
  tooltip.close();
  mobiscroll.toast({
    message: 'Appointment deleted',
  });
});
```

### HTML

```html
<div id="demo-tooltip-calendar"></div>
<div id="demo-tooltip-popup" class="mds-tooltip mds-popup">
  <div id="demo-tooltip-header" class="mds-tooltip-header">
    <span id="demo-tooltip-info"></span>
    <span id="demo-tooltip-time" class="mbsc-pull-right"></span>
  </div>
  <div class="mbsc-padding">
    <div class="mds-tooltip-label mbsc-margin">
      Status: <span id="demo-tooltip-status" class="mbsc-light"></span>
      <button id="demo-tooltip-status-update" mbsc-button data-color="warning" data-variant="outline" class="mds-tooltip-button mbsc-pull-right">
      </button>
    </div>
    <div class="mds-tooltip-label mbsc-margin">Reason for visit: <span id="demo-tooltip-reason" class="mbsc-light"></span></div>
    <div class="mds-tooltip-label mbsc-margin">Location: <span id="demo-tooltip-location" class="mbsc-light"></span></div>
    <button id="demo-tooltip-view" mbsc-button data-color="secondary" class="mds-tooltip-button">
      View patient file
    </button>
    <button id="demo-tooltip-delete" mbsc-button data-color="danger" data-variant="outline" class="mds-tooltip-button mbsc-pull-right">
      Delete appointment
    </button>
  </div>
</div>
```

### CSS

```css
.mds-tooltip {
  font-size: 15px;
  font-weight: 600;
}

.mds-tooltip-header {
  padding: 12px 16px;
  color: #eee;
}

.mds-tooltip-label {
  line-height: 32px;
}

.mds-tooltip-button.mbsc-button {
  font-size: 16px;
  margin: 0;
  line-height: 32px;
  padding-top: 0;
  padding-bottom: 0;
}

.mds-tooltip-button .mbsc-button-txt {
  font-size: 14px;
  padding-left: 0;
  padding-right: 0;
}
```

## Implementation notes

- Set `view: { calendar: { type: 'week' } }`. Set `showEventTooltip: false` to suppress the native browser tooltip. Set `dragToMove: true` and disable `clickToCreate`, `dragToCreate`, and `dragToResize`.
- Each event object carries custom fields alongside the standard ones: `age`, `confirmed`, `reason`, and `location`. Use `formatDate` from Mobiscroll to format `event.start` and `event.end` as human-readable time strings for the tooltip header.
- Render a Mobiscroll `Popup` outside the calendar with `display="anchored"`, `showOverlay={false}`, `scrollLock={false}`, and `touchUi={false}`. In `onEventHoverIn` (Vue: `@event-hover-in`), set the popup anchor to `args.domEvent.target` and open it. The same `openTooltip` logic also runs on `onEventClick` so the tooltip appears on both hover and tap.
- Implement a hover-bridge timer: `onEventHoverOut` (Vue: `@event-hover-out`) starts a 200ms timer to close the popup; `onMouseEnter` on the popup div cancels the timer so the user can move the mouse from the event into the tooltip without it disappearing; `onMouseLeave` on the popup div starts a new 200ms timer. Close immediately on `onEventDragStart`. Angular: use `@ViewChild` (Angular: `@ViewChild`) to get the `MbscPopup` instance and call `.open()` / `.close()` imperatively.
- Inside the popup, build a header using the event's `color` as background, and a body with Status (derived from `event.confirmed`), Reason for visit, and Location rows. Add a confirm/cancel toggle `Button` in the Status row, a `View patient file` button (`color="secondary"`), and a `Delete appointment` button (`color="danger"`, `variant="outline"`). Each action closes the tooltip and shows a `Toast`; delete also removes the event from the data array.

## 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: [Custom Header](https://demo.mobiscroll.com/javascript/eventcalendar/customizing-header.md) - Customize the Javascript event calendar header — rearrange navigation, add a Today button, and switch between calendar and scheduler views.
- Next: [Add Event on Hover](https://demo.mobiscroll.com/javascript/eventcalendar/cell-template-on-hover.md) - Show an "Add event" button on day cell hover in a Javascript event calendar — reveal event creation without cluttering the calendar interface.
