Overview
How to configure the JavaScript Scheduler to allow a time range selection over multiple adjacent rows.
The implementation uses rectangle selection in custom mode and creates a Scheduler multi-range selection while the user is dragging.
The source code includes a trial version of DayPilot Pro for JavaScript (see License below).
You can also open the JavaScript Scheduler time-range selection demo.
License
Licensed for testing and evaluation purposes. Please see the license agreement included in the sample package. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.
JavaScript Scheduler: Rectangle Selection

In addition to the standard drag and drop operations, the Scheduler supports selecting a custom rectangle area. It has to be enabled using the rectangleSelectHandling property.
Selecting Events using the Rectangle
The built-in rectangle action can select all events that overlap the dragged area. Set rectangleSelectHandling to "EventSelect" when you want the Scheduler to manage the event selection for you.
rectangleSelectHandling: "EventSelect",
rectangleSelectMode: "Free",Shift + drag activates the rectangle selection and displays the corresponding rectangle:

The underlying events are selected when you release the mouse button:

Selecting a Time Range
We will use the same rectangle gesture to create a custom time range selection that spans multiple adjacent rows. In this case, set rectangleSelectHandling to "Enabled" so the Scheduler fires the rectangle events without applying the default event-selection action.
rectangleSelectMode: "Free",
rectangleSelectHandling: "Enabled",Next, add an onRectangleSelecting event handler. This handler runs while the user is dragging. It hides the normal rectangle overlay and replaces it with a time range multi-selection, one range for each resource row under the rectangle.
onRectangleSelecting: (args) => {
args.visible = false;
dp.multirange.clear();
args.resources.forEach((resource) => {
dp.multirange.add({start: args.start, end: args.end, resource});
});
},The call to args.visible = false hides the rectangle itself. The visible feedback comes from the multi-range selection created by dp.multirange.add().
Handling the Multi-Row Selection

Normally, you would use the onTimeRangeSelect event to handle time-range selections. In this case, that event is not fired by the rectangle gesture because the selected ranges are created using the API.
Instead, use the onRectangleSelect event handler. It fires when the user releases the mouse button, after the final rectangle coordinates and resource list are known.
onRectangleSelect: () => {
dp.multirange.get().forEach((range) => {
if (!range.allowed) {
return;
}
dp.events.add({
start: range.start,
end: range.end,
resource: range.resource,
text: "Event",
id: DayPilot.guid()
});
});
dp.multirange.clear();
}The example creates a new event in every selected row where the range is allowed. With allowEventOverlap set to false, conflicting rows are skipped. Finally, dp.multirange.clear() removes the temporary selection highlight.
Full Source Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JavaScript Scheduler: Select a Time Range over Multiple Rows</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/85404/javascript-scheduler-select-a-time-range-over-multiple-rows'>JavaScript Scheduler: Select a Time Range over Multiple Rows</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">Use Shift+drag to select a time range for multiple rows.</div>
<div id="dp"></div>
<div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>
<script>
const monthStart = DayPilot.Date.today().firstDayOfMonth();
const dp = new DayPilot.Scheduler("dp", {
timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
scale: "Day",
days: monthStart.daysInMonth(),
startDate: monthStart,
timeRangeSelectedHandling: "Enabled",
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
});
},
rectangleSelectMode: "Free",
rectangleSelectHandling: "Enabled",
allowEventOverlap: false,
onRectangleSelecting: (args) => {
args.visible = false;
dp.multirange.clear();
args.resources.forEach((resource) => {
dp.multirange.add({start: args.start, end: args.end, resource});
});
},
onRectangleSelect: () => {
dp.multirange.get().forEach((range) => {
if (!range.allowed) {
return;
}
dp.events.add({
start: range.start,
end: range.end,
resource: range.resource,
text: "Event",
id: DayPilot.guid()
});
});
dp.multirange.clear();
}
});
dp.init();
const app = {
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 = [
{text: "Event 1", start: monthStart.addDays(11), end: monthStart.addDays(16), resource: "R3", id: 1},
{text: "Event 2", start: monthStart.addDays(15), end: monthStart.addDays(20), resource: "R5", id: 2},
{text: "Event 3", start: monthStart.addDays(9), end: monthStart.addDays(15), resource: "R7", id: 3},
];
dp.update({resources, events});
}
};
app.init();
</script>
</body>
</html>
DayPilot




