Overview
The JavaScript Scheduler component can copy several events at once.
Copy events from multiple Scheduler rows using Ctrl+drag.
In addition to the drag-and-drop method, you can use “Copy” and “Paste” context menu items.
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.
Allow Selection of Multiple Events
First, set the allowMultiSelect property to true to enable selection of multiple events.
allowMultiSelect: true,Now set the event click action to "Select" using the eventClickHandling property. This allows event selection on click. Multiple events can be selected using Ctrl+click.
eventClickHandling: "Select",Copy Multiple Events using Drag and Drop

Enable moving of multiple selected events using the allowMultiMove property of the JavaScript Scheduler component.
Set multiMoveVerticalMode to "All". This ensures that all selected events can be moved vertically.
In the onEventMove event handler, which is fired before the event position is updated after drop, detect the Ctrl/Meta key state. If the modifier key is pressed, copy the events instead of moving them:
Cancel the default action, the update of the selected event positions, using
args.preventDefault().Create new events using events.add() at the target position. Selected properties of the source events can be copied here; this sample changes the event text to show that the new events are copies.
The new events must have different IDs because duplicate event IDs are not allowed. In this example, the client generates random IDs. In a database-backed application, you would normally call the API and use the IDs generated by the database.
In this case, you can’t use the onEventMoved event handler because it is fired after the events have already been updated.
allowMultiMove: true,
multiMoveVerticalMode: "All",
onEventMove: args => {
const copy = args.ctrl || args.meta;
if (copy) {
args.preventDefault();
args.multimove.forEach(item => {
dp.events.add({
start: item.start,
end: item.end,
id: DayPilot.guid(),
text: "Copy of " + item.event.text(),
resource: item.resource
});
});
}
},Copy Events using Context Menu (Copy & Paste)

The event context menu, defined using the contextMenu property, includes a “Copy” item.
The handler stores the selected events in the sample clipboard. The clipboard is part of the app object so the auxiliary sample state stays separate from the Scheduler configuration.
contextMenu: new DayPilot.Menu({
items: [
{
text: "Copy",
onClick: args => {
app.clipboard = dp.multiselect.events();
}
}
]
}),Now add the “Paste” item to the time range selection context menu using contextMenuSelection:
contextMenuSelection: new DayPilot.Menu({
onShow: args => {
const pasteEnabled = app.clipboard.length > 0;
dp.contextMenuSelection.items[0].disabled = !pasteEnabled;
dp.contextMenuSelection.items[1].disabled = !pasteEnabled;
},
items: [
{
text: "Paste",
onClick: args => {
const ref = app.clipboard[0];
const refRowIndex = dp.rows.find(ref.data.resource).index;
const selection = args.source;
const targetRowIndex = dp.rows.find(selection.resource).index;
const events = app.clipboard.map(e => {
const srcRowIndex = dp.rows.find(e.data.resource).index;
const rowOffset = srcRowIndex - refRowIndex;
const rowIndex = targetRowIndex + rowOffset;
const cell = dp.cells.findXy(0, rowIndex)[0];
if (!cell) {
return null;
}
const resource = dp.cells.findXy(0, rowIndex)[0].resource;
const duration = e.duration(); // milliseconds
const offset = e.start().getTime() - ref.start().getTime();
const start = selection.start.addTime(offset);
const end = start.addTime(duration);
return new DayPilot.Event({
start,
end,
text: "Copy of " + e.text(),
resource,
id: DayPilot.guid()
});
}).filter(e => e !== null);
events.forEach(e => {
dp.events.add(e);
});
dp.clearSelection();
}
},
{
text: "Paste into the same row",
onClick: args => {
const ref = app.clipboard[0];
const selection = args.source;
app.clipboard.forEach(e => {
const duration = e.duration(); // milliseconds
const offset = e.start().getTime() - ref.start().getTime();
const start = selection.start.addTime(offset);
const end = start.addTime(duration);
const newEvent = new DayPilot.Event({
start,
end,
text: "Copy of " + e.text(),
resource: selection.resource,
id: DayPilot.guid() // generate random id
});
dp.events.add(newEvent);
});
dp.clearSelection();
}
}
]
})The onClick handler of the “Paste” item calculates the relative row and time position for each event in the clipboard and creates a corresponding new event in the JavaScript Scheduler.
For reference, the context menu also includes “Paste into the same row,” which places all copied events in the selected target row.
Full Source Code
Here is the complete source code of the example that configures the JavaScript Scheduler to copy multiple events using a context menu and drag and drop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JavaScript Scheduler: Copy Multiple Events</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; }
.generated a { color: #999; }
</style>
<!-- DayPilot library -->
<script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
<h1><a href='https://code.daypilot.org/72047/javascript-scheduler-copy-multiple-events'>JavaScript Scheduler: Copy Multiple Events</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="space">
Select multiple events using Ctrl+click (or <a href="javascript: dp.events.all().forEach(e => dp.multiselect.add(e))">select all</a>)
and right-click to copy and paste. You can also copy events using Ctrl+drag.
</div>
<div id="dp"></div>
<div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>
<script>
const app = {
clipboard: [],
init() {
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 = [
{start: "2026-05-05T00:00:00", end: "2026-05-10T00:00:00", id: "d0d4fe15-edde-728e-d75b-d31b36a9e1eb", resource: "R3", text: "Event 1"},
{start: "2026-05-08T00:00:00", end: "2026-05-13T00:00:00", id: "60e400c6-0dc9-cf4e-25b3-1d955670fe0d", resource: "R4", text: "Event 2"},
{start: "2026-05-10T00:00:00", end: "2026-05-12T00:00:00", id: "6d96bfcb-51ce-4406-45e7-e82ff824a3b7", resource: "R5", text: "Event 3"},
];
dp.update({resources, events});
}
};
const dp = new DayPilot.Scheduler("dp", {
timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
scale: "Day",
days: 30,
startDate: "2026-05-01",
timeRangeSelectedHandling: "Enabled",
allowMultiSelect: true,
onTimeRangeSelected: async args => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
dp.clearSelection();
if (modal.canceled) { return; }
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
});
},
eventClickHandling: "Select",
allowMultiMove: true,
multiMoveVerticalMode: "All",
treeEnabled: true,
onEventMove: args => {
const copy = args.ctrl || args.meta;
if (copy) {
args.preventDefault();
args.multimove.forEach(item => {
dp.events.add({
start: item.start,
end: item.end,
id: DayPilot.guid(),
text: "Copy of " + item.event.text(),
resource: item.resource
});
});
}
},
contextMenu: new DayPilot.Menu({
items: [
{
text: "Copy",
onClick: args => {
app.clipboard = dp.multiselect.events();
}
}
]
}),
contextMenuSelection: new DayPilot.Menu({
onShow: args => {
const pasteEnabled = app.clipboard.length > 0;
dp.contextMenuSelection.items[0].disabled = !pasteEnabled;
dp.contextMenuSelection.items[1].disabled = !pasteEnabled;
},
items: [
{
text: "Paste",
onClick: args => {
const ref = app.clipboard[0];
const refRowIndex = dp.rows.find(ref.data.resource).index;
const selection = args.source;
const targetRowIndex = dp.rows.find(selection.resource).index;
const events = app.clipboard.map(e => {
const srcRowIndex = dp.rows.find(e.data.resource).index;
const rowOffset = srcRowIndex - refRowIndex;
const rowIndex = targetRowIndex + rowOffset;
const cell = dp.cells.findXy(0, rowIndex)[0];
if (!cell) {
return null;
}
const resource = dp.cells.findXy(0, rowIndex)[0].resource;
const duration = e.duration(); // milliseconds
const offset = e.start().getTime() - ref.start().getTime();
const start = selection.start.addTime(offset);
const end = start.addTime(duration);
return new DayPilot.Event({
start,
end,
text: "Copy of " + e.text(),
resource,
id: DayPilot.guid()
});
}).filter(e => e !== null);
events.forEach(e => {
dp.events.add(e);
});
dp.clearSelection();
}
},
{
text: "Paste into the same row",
onClick: args => {
const ref = app.clipboard[0];
const selection = args.source;
app.clipboard.forEach(e => {
const duration = e.duration(); // milliseconds
const offset = e.start().getTime() - ref.start().getTime();
const start = selection.start.addTime(offset);
const end = start.addTime(duration);
const newEvent = new DayPilot.Event({
start,
end,
text: "Copy of " + e.text(),
resource: selection.resource,
id: DayPilot.guid() // generate random id
});
dp.events.add(newEvent);
});
dp.clearSelection();
}
}
]
})
});
dp.init();
app.init();
</script>
</body>
</html>
History
May 13, 2026: Updated to DayPilot Pro for JavaScript 2026.2. Updated visible demo dates and article code snippets.
DayPilot




