Features
How to disable custom date/time range and prevent drag and drop operations (JavaScript Scheduler component).
Multiple disabled ranges per row supported
Independent data for each Scheduler row
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.
Live Demo
Disabled Cells
The JavaScript Scheduler component supports disabling cells for drag and drop operations. The users will not be able to create new events on top of the disabled cells. It will not be possible to move or resize existing events to the disabled areas.
The disabling works on cell level. To disable a cell, set args.cell.disabled to true in onBeforeCellRender event handler:
onBeforeCellRender: (args) => {
if (args.cell.start < new DayPilot.Date("2024-03-05") || args.cell.resource === "D") {
args.cell.disabled = true;
args.cell.backColor = "#ccc";
}
};
However, the Scheduler is flexible enough to allow finer control of the disabled ranges. This tutorial shows how to create a custom disabled date/time range that is not aligned with the grid cells.
Disabled Range Data
We will define the disabled range data using disabled
property of the resources array when loading resources. This is a custom property and you can change its name as needed.
The disabled
property holds an array of objects, each with start
, end
and backColor
property defined:
const app = {
loadData() {
const resources = [
{name: "Resource 1", id: "R1", disabled: [
{ start: "2025-12-03T12:00:00", end: "2025-12-05T12:00:00", backColor: "#d9ead3"}
]
},
{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"}
];
scheduler.update({resources});
},
};
Partially Disabled Cells: Highlighting
In order to highlight the disabled areas, we will use onBeforeCellRender event handler.
The event handler checks if there are any disabled ranges defined for the current row:
onBeforeCellRender: (args) => {
const row = scheduler.rows.find(args.cell.resource);
const disabled = row.data.disabled;
if (!disabled) {
return;
}
// ...
}
Then it looks for ranges that overlap the current cell:
const item = disabled.map((item) => ({
start: new DayPilot.Date(item.start),
end: new DayPilot.Date(item.end),
backColor: item.backColor
})).find((item) =>
DayPilot.Util.overlaps(item.start, item.end, args.cell.start, args.cell.end)
);
If an overlapping range is found, it checks whether the area covers the whole cell or just a part of it. For performance reasons, it uses args.cell.backColor
to set the cell background for the whole cell.
For partial overlaps, it inserts an active area to highlight the affected part of the cell:
if (item) {
const fullOverlap = item.start <= args.cell.start && item.end >= args.cell.end;
if (fullOverlap) {
args.cell.backColor = item.backColor;
}
else {
args.cell.areas = [
{ start: item.start, end: item.end, top: 0, bottom: 0, backColor: item.backColor}
];
}
}
The complete onBeforeCellRender
event handler:
onBeforeCellRender: (args) => {
const row = scheduler.rows.find(args.cell.resource);
const disabled = row.data.disabled;
if (!disabled) {
return;
}
const item = disabled.map((item) => ({
start: new DayPilot.Date(item.start),
end: new DayPilot.Date(item.end),
backColor: item.backColor
})).find((item) =>
DayPilot.Util.overlaps(item.start, item.end, args.cell.start, args.cell.end)
);
if (item) {
const fullOverlap = item.start <= args.cell.start && item.end >= args.cell.end;
if (fullOverlap) {
args.cell.backColor = item.backColor;
}
else {
args.cell.areas = [
{ start: item.start, end: item.end, top: 0, bottom: 0, backColor: item.backColor}
];
}
}
},
Partially Disabled Cells: Preventing Drag and Drop
In addition to highlighting the disabled areas, it's also necessary to prevent drag and drop operations. That can be done using the real-time drag and drop events:
The logic of all event handlers is identical: It looks for disabled areas using disabled
property of the resource object and it checks for overlap with the current drag and drop target. If an overlap is found, the current target is marked as forbidden using args.allowed.
Creating events (time range selecting):
onTimeRangeSelecting: (args) => {
const disabled = args.row.data.disabled;
if (!disabled) {
return;
}
const item = disabled.map((item) => ({
start: new DayPilot.Date(item.start),
end: new DayPilot.Date(item.end)
})).find((item) =>
DayPilot.Util.overlaps(item.start, item.end, args.start, args.end)
);
if (item) {
args.allowed = false;
}
}
Moving events:
onEventMoving: (args) => {
const disabled = args.row.data.disabled;
if (!disabled) {
return;
}
const item = disabled.map((item) => ({
start: new DayPilot.Date(item.start),
end: new DayPilot.Date(item.end)
})).find((item) =>
DayPilot.Util.overlaps(item.start, item.end, args.start, args.end)
);
if (item) {
args.allowed = false;
}
},
Resizing events:
onEventResizing: (args) => {
const disabled = args.row.data.disabled;
if (!disabled) {
return;
}
const item = disabled.map((item) => ({
start: new DayPilot.Date(item.start),
end: new DayPilot.Date(item.end)
})).find((item) =>
DayPilot.Util.overlaps(item.start, item.end, args.start, args.end)
);
if (item) {
args.allowed = false;
}
},