Overview

  • Add a context menu to JavaScript Scheduler events with “Lock” and “Unlock” items.

  • Users will not be able to move or change the locked events

  • The context menu is updated dynamically depending on the event status.

  • Includes the open-source DayPilot Lite for JavaScript scheduling library

Context Menu with “Lock” Item

JavaScript Scheduler - Lock Events using Context Menu Item

Lets define a context menu that will allow users lock Scheduler events:

const scheduler = new DayPilot.Scheduler("dp", {

  // ...

  contextMenu: new DayPilot.Menu({
    items: [
      {
        text: "Lock",
        onClick: (args) => {
          const e = args.source;
          const locked = e.data.tags?.locked;
          if (!e.data.tags) {
            e.data.tags = {};
          }
          e.data.tags.locked = !locked;
          scheduler.events.update(e);
        }
      },
      {
        text: "-"
      },
      {
        text: "Edit...",
        onClick: (args) => {
          app.editEvent(args.source);
        }
      },
    ],
  }),

  // ...

});

The onClick event handler of the “Lock” menu item sets the locked custom property to true. The custom property is saved under tags object of the event data object to prevent naming conflicts.

Locking the JavaScript Scheduler Event

We will lock the item using onBeforeEventRender.

const scheduler = new DayPilot.Scheduler("dp", {
  
  // ...

  onBeforeEventRender: (args) => {
    const locked = args.data.tags?.locked;
    if (locked) {
      args.data.moveDisabled = true;
      args.data.resizeDisabled = true;
      args.data.clickDisabled = true;
    }
  },
  
  // ...
  
});

If the “locked” status is detected (args.data.tags.locked) the drag and drop event operations (moving, resizing) are disabled, as well as the click action.

For more options on making the Scheduler events (or the whole Scheduler) read-only please see the following tutorial:

Locked Event Icon

JavaScript Scheduler - Locked Event Icon

We will also use onBeforeEventRender event handler to display a padlock icon inside the locked event:

const scheduler = new DayPilot.Scheduler("dp", {
  
  // ...

  onBeforeEventRender: (args) => {
    const locked = args.data.tags?.locked;
    if (locked) {

      // ...

      args.data.areas = [
        {
          top: 10,
          right: 10,
          width: 15,
          height: 15,
          symbol: "icons/daypilot.svg#padlock"
        }
      ];
    }
  },
  
  // ...
  
});

Unlocking the Scheduler Event

JavaScript Scheduler - Unlocking the Event

We will use the onShow event of the context menu object to change the context menu items depending on the event status.

If the event is locked, the following changes will be made:

  • The first menu item (“Lock”) text will be changed to “Unlock”

  • The “Edit…” item will be disabled

const scheduler = new DayPilot.Scheduler("dp", {

  // ...

  contextMenu: new DayPilot.Menu({
    items: [
      {
        text: "Lock",
        onClick: (args) => {
          const e = args.source;
          const locked = e.data.tags?.locked;
          if (!e.data.tags) {
            e.data.tags = {};
          }
          e.data.tags.locked = !locked;
          scheduler.events.update(e);
        }
      },
      {
        text: "-"
      },
      {
        text: "Edit...",
        onClick: (args) => {
          app.editEvent(args.source);
        }
      },
    ],
    onShow: (args) => {
      const e = args.source;
      const locked = e.data.tags && e.data.tags.locked;
      scheduler.contextMenu.items[0].text = locked ? "Unlock" : "Lock";
      scheduler.contextMenu.items[2].disabled = locked;
    }
  }),

  // ...

});

Full Source Code

This is the full source code of our example that adds a context menu to the JavaScript Scheduler events with an option to lock the event.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JavaScript Scheduler: Lock Events using Context Menu</title>

  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/30746/javascript-scheduler-lock-events-using-context-menu'>JavaScript Scheduler: Lock Events using Context Menu</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 id="dp"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>

<script>
  const scheduler = new DayPilot.Scheduler("dp", {
    timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
    scale: "Day",
    days: 31,
    startDate: "2026-08-01",
    eventBorderRadius: 30,
    durationBarVisible: false,
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected:  async (args) => {
      const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
      scheduler.clearSelection();
      if (modal.canceled) {
        return;
      }
      scheduler.events.add({
        start: args.start,
        end: args.end,
        id: DayPilot.guid(),
        resource: args.resource,
        text: modal.result
      });
    },
    onBeforeEventRender: (args) => {
      args.data.backColor = "#f1c232aa";
      args.data.borderColor = "darker";
      args.data.padding = 10;

      const locked = args.data.tags?.locked;
      if (locked) {
        args.data.moveDisabled = true;
        args.data.resizeDisabled = true;
        args.data.clickDisabled = true;
        args.data.areas = [
          {
            top: 10,
            right: 10,
            width: 15,
            height: 15,
            symbol: "icons/daypilot.svg#padlock"
          }
        ];
      }
    },
    onEventClick: (args) => {
      editEvent(args.e);
    },
    contextMenu: new DayPilot.Menu({
      items: [
        {
          text: "Lock",
          onClick: (args) => {
            const e = args.source;
            const locked = e.data.tags?.locked;
            if (!e.data.tags) {
              e.data.tags = {};
            }
            e.data.tags.locked = !locked;
            scheduler.events.update(e);
          }
        },
        {
          text: "-"
        },
        {
          text: "Edit...",
          onClick: (args) => {
            app.editEvent(args.source);
          }
        },
      ],
      onShow: (args) => {
        const e = args.source;
        const locked = e.data.tags && e.data.tags.locked;
        scheduler.contextMenu.items[0].text = locked ? "Unlock" : "Lock";
        scheduler.contextMenu.items[2].disabled = locked;
      }
    }),
  });
  scheduler.init();

  const app = {
    async editEvent(e) {
      const form = [
        {name: "Name", id: "text"}
      ];
      const data = e.data;
      const modal = await DayPilot.Modal.form(form, data);
      if (modal.canceled) {
        return;
      }
      e.data = modal.result;
      scheduler.events.update(e);
    },
    loadData() {
      const resources = [
        {name: "Resource 1", id: "R1"},
        {name: "Resource 2", id: "R2"},
        {name: "Resource 3", id: "R3"},
        {name: "Resource 4", id: "R4"},
        {name: "Resource 5", id: "R5"},
        {name: "Resource 6", id: "R6"},
        {name: "Resource 7", id: "R7"},
        {name: "Resource 8", id: "R8"},
        {name: "Resource 9", id: "R9"},
      ];

      const events = [
        {id: 1, start: "2026-08-05T00:00:00", end: "2026-08-10T00:00:00", text: "Event 1", resource: "R2"}
      ];

      scheduler.update({resources, events});
    }
  };
  app.loadData();
</script>

</body>
</html>