Features
JavaScript Scheduler component from DayPilot Pro for JavaScript package
The Scheduler loads events dynamically during scrolling
As soon as the scrollbar reaches an edge of the visible range it is updated to allow further scrolling
Includes a trial version of DayPilot Pro for JavaScript (see License below)
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.
Infinite Scrolling Feature
The Scheduler infinite scrolling feature is disabled by default. You can enable it using infiniteScrollingEnabled property:
{
infiniteScrollingEnabled: true,
// ...
}
Infinite Scrolling Configuration
The infinite scrolling mode uses the following mechanism:
As soon as a user reaches a scheduler edge (within a specified margin) it automatically shifts the displayed range. The margin is defined using infiniteScrollingMargin property. The default value is 50 (pixels). We will set the value to a half of the current window width.
{
infiniteScrollingMargin: window.innerWidth /2,
// ...
}
When the date shift is activated, the Scheduler updates the value of startDate
and refreshes the view. The scrollbar position is updated to display the original viewport. The days
property remains unchanged. The startDate
property is shifted by the number of days specified using infiniteScrollingStepDays property.
{
infiniteScrollingStepDays: 100,
// ...
}
Loading Events After the Date Shift
It's possible to use preload the Scheduler events using dp.events.list
but it's better to enable dynamic event loading. This way the Scheduler will always load events needed for the current viewport.
{
dynamicLoading: true,
// ...
}
With dynamic loading enabled, we will only have a limited event set loaded at each moment. To improve the rendering speed during scrolling, we will disable the progressive event rendering.
{
dynamicEventRendering: "Disabled",
// ...
}
The events can be loaded using the onScroll event handler which is fired whenever the scrollbar position changes:
{
onScroll: async (args) => {
const { start, end } = args.viewport;
const visibleRange = new DayPilot.Duration(start, end);
const params = {
start: start.addTime(-visibleRange.ticks),
end: end.addTime(visibleRange.ticks)
};
const {data} = await DayPilot.Http.post("backend_events.php", params);
args.events = data;
args.loaded();
},
// ...
}
As you can see, we extend the viewport using addTime()
to preload events for adjacent areas.
Scrolling API
Previous/Today/Next Buttons
The scrollTo() method allows scrolling within the active range. It will also automatically switch the current date range if necessary.
HTML5
<button id="previous">Previous</button>
<button id="today">Today</button>
<button id="next">Next</button>
JavaScript
const app = {
elements: {
previous: document.querySelector("#previous"),
today: document.querySelector("#today"),
next: document.querySelector("#next"),
},
init() {
this.setupNavigation();
this.loadResources();
},
setupNavigation() {
this.elements.previous.addEventListener("click", () => {
scheduler.scrollTo(scheduler.getViewPort().start.addMonths(-1), "fast");
});
this.elements.today.addEventListener("click", () => {
const today = DayPilot.Date.today();
scheduler.scrollTo(today, "fast");
});
this.elements.next.addEventListener("click", () => {
scheduler.scrollTo(scheduler.getViewPort().start.addMonths(1), "fast");
});
},
async loadResources() {
scheduler.rows.load("backend_resources.php");
}
};
app.init();
We have configured the Next/Previous buttons to shift the viewport by one month. As soon as the viewport reaches the visible range edge the Scheduler will update the timeline to allow further scrolling.
Full Source Code
Here is the full source code of our PHP app that enables infinite scrolling for the JavaScript Scheduler component.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>HTML5 Scheduler: Infinite Scrolling</title>
<style>
.scheduler_default_event,
.scheduler_default_event_inner {
border-radius: 10px;
}
body .scheduler_default_event_inner {
border: 2px solid #3498db;
background: #3498db;
color: white;
padding: 5px;
}
body .scheduler_default_event_inner:hover {
background: #2980b9;
}
</style>
<link rel="stylesheet" type="text/css" href="buttons.css"/>
<!-- DayPilot library -->
<script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
<h1><a href='https://code.daypilot.org/81725/html5-scheduler-infinite-scrolling'>HTML5 Scheduler: Infinite Scrolling</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="buttons">
<button id="previous">Previous</button>
<button id="today">Today</button>
<button id="next">Next</button>
</div>
<div id="scheduler"></div>
</div>
<script>
const scheduler = new DayPilot.Scheduler("scheduler", {
scale: "Day",
timeHeaders: [
{ groupBy: "Month" },
{ groupBy: "Day", format: "d" }
],
durationBarVisible: false,
rowMarginBottom: 2,
rowMarginTop: 2,
infiniteScrollingEnabled: true,
infiniteScrollingMargin: window.innerWidth / 2,
infiniteScrollingStepDays: 100,
dynamicEventRendering: "Disabled",
days: 300,
treeEnabled: true,
dynamicLoading: true,
onTimeRangeSelected: async (args) => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
scheduler.clearSelection();
if (modal.canceled) {
return;
}
const name = modal.result;
const params = {
start: args.start,
end: args.end,
resource: args.resource,
name: name
};
const {data} = await DayPilot.Http.post("backend_create.php", params);
const event = {
start: args.start,
end: args.end,
id: data.id,
resource: args.resource,
text: name
};
scheduler.events.add(event);
scheduler.message(data.message);
},
onScroll: async (args) => {
const { start, end } = args.viewport;
const visibleRange = new DayPilot.Duration(start, end);
const params = {
start: start.addTime(-visibleRange.ticks),
end: end.addTime(visibleRange.ticks)
};
const {data} = await DayPilot.Http.post("backend_events.php", params);
args.events = data;
args.loaded();
},
onEventMoved: async (args) => {
const params = {
id: args.e.id(),
newStart: args.newStart,
newEnd: args.newEnd,
newResource: args.newResource
};
await DayPilot.Http.post("backend_move.php", params);
scheduler.message("Moved.");
},
onEventResized: async (args) => {
const params = {
id: args.e.id(),
newStart: args.newStart,
newEnd: args.newEnd,
newResource: args.e.resource()
};
await DayPilot.Http.post("backend_move.php", params);
scheduler.message("Resized.");
},
separators: [
{ location: DayPilot.Date.now(), color: "red" }
],
});
scheduler.init();
const app = {
elements: {
previous: document.querySelector("#previous"),
today: document.querySelector("#today"),
next: document.querySelector("#next"),
},
init() {
this.setupNavigation();
this.loadResources();
},
setupNavigation() {
this.elements.previous.addEventListener("click", () => {
scheduler.scrollTo(scheduler.getViewPort().start.addMonths(-1), "fast");
});
this.elements.today.addEventListener("click", () => {
const today = DayPilot.Date.today();
scheduler.scrollTo(today, "fast");
});
this.elements.next.addEventListener("click", () => {
scheduler.scrollTo(scheduler.getViewPort().start.addMonths(1), "fast");
});
},
async loadResources() {
scheduler.rows.load("backend_resources.php");
}
};
app.init();
</script>
</body>
</html>
You can download the PHP project (including the backend PHP scripts that handle UI events and save changes to the database) at the top of this article.
History
July 12, 2024: Upgraded to DayPilot Pro 2024.3.5967; PHP 8+ compatibility, ES6 syntax with async/away; styling updated.
December 15, 2020: Upgraded to DayPilot Pro 2020.4.4808; jQuery dependency removed; loading events for extended viewport
April 4, 2018: Upgraded to DayPilot Pro 2018.2.3217 with fast cell rendering
November 6, 2017: Initial release