Overview
Requires DayPilot Pro for JavaScript 2020.4.4755 or later
The Scheduler supports keyboard navigation in the grid
You can select a cell using <enter> key
You can select multiple cells in a row using Shift + left or Shift + right
Pressing <enter> when an existing event is focused opens an edit dialog
Keyboard navigation can be used in combination with inline event editing
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.
Live Demo
Enable Keyboard Navigation for the JavaScript Scheduler
You can enable keyboard navigation in the JavaScript Scheduler component using keyboardNavigation
property:
var dp = new DayPilot.Scheduler("dp", {
keyboardEnabled: true,
// ...
});
By default, the key event handler are attached to the document element. You can override it using keyboardTarget property. The the other possible value is "component" which binds the events to the main Scheduler element:
var dp = new DayPilot.Scheduler("dp", {
keyboardEnabled: true,
keyboardTarget: "component",
// ...
});
When the Scheduler with keyboard access is initialized, no cell is focused and the view is standard:
As soon as you press one of the arrow keys, the cell which is displayed in the upper-left corner of the current Scheduler viewport will be focused:
Now you can navigate the Scheduler grid using arrow keys.
Create Events using Keyboard
If you press <enter> the Scheduler will create a time range selection for the current cell and fire onTimeRangeSelect and onTimeRangeSelected event handler. You can use them to create a new event:
Our onTimeRangeSelected
event handler opens a modal dialog using DayPilot.Modal dialog:
onTimeRangeSelected: function (args) {
DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
dp.clearSelection();
if (!modal.result) { return; }
dp.events.add(new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
}));
});
},
You can also create a time range spanning multiple cells in a row using Shift + left or Shift + right:
Edit Events using Keyboard
When there is an existing event at the current location the Scheduler will automatically focus it:
If you press <enter> the Scheduler will fire onEventClick and onEventClicked events. You can use onEventClick to open a modal dialog that lets you edit the event details:
And this is our onEventClick
event handler:
onEventClick: function(args) {
DayPilot.Modal.prompt("Edit event:", args.e.data.text).then(function(modal) {
if (modal.canceled) {
return;
}
args.e.data.text = modal.result;
dp.events.update(args.e);
});
},
Keyboard Navigation and Inline Editing
It is also possible to use inline event editing in combination with the keyboard navigation. The Scheduler will display an edit box right at the focused location:
In order to enable inline editing, the event handlers needs to be modified as follows:
onTimeRangeSelected: function (args) {
dp.clearSelection();
var e = new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: "New event",
temporary: true
});
dp.events.add(e);
dp.events.edit(e);
},
onEventEdited: function(args) {
if (args.canceled && args.e.data.temporary) {
dp.events.remove(args.e);
}
else {
args.e.data.temporary = false;
}
},
onEventClick: function(args) {
dp.events.edit(args.e);
},
The onTimeRangeSelected
handler now creates a temporary event and immediately activates the editing mode. We have also added onEventEdited
event handler to handle the <esc>
key during event creation - this way the temporary event can be removed.
Full Source Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JavaScript Scheduler: Keyboard Navigation</title>
<style type="text/css">
p, body, td { font-family: Tahoma, Arial, Helvetica, sans-serif; font-size: 10pt; }
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>
<style>
.hints {
border: 1px solid #ccc;
}
.hints td {
font-size: 14px;
padding: 5px;
}
.key {
display: inline-block;
padding: 2px 3px 3px 3px;
border: 1px solid #ccc;
border-radius: 2px;
min-width: 20px;
text-align: center;
background-color: #f0f0f0;
}
.toolbar {
margin: 10px 0px;
font-size: 14px;
display: flex;
align-items: center;
}
.toolbar select {
font-size: 14px;
padding: 3px;
}
.toolbar-item {
display: flex;
align-items: center;
margin-left: 5px;
}
.toolbar-separator {
width: 1px;
height: 28px;
display: inline-block;
box-sizing: border-box;
background-color: #ccc;
margin-bottom: -8px;
margin-left: 15px;
margin-right: 15px;
}
</style>
<!-- DayPilot library -->
<script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
<h1><a href='https://code.daypilot.org/29708/javascript-scheduler-keyboard-navigation'>JavaScript Scheduler: Keyboard Navigation</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">
<table class="hints">
<tr>
<td>Move:</td>
<td><span class="key">←</span> <span class="key">→</span> <span class="key">↓</span> <span class="key">↑</span></td>
</tr>
<tr>
<td>Create an event:</td>
<td><span class="key">↵ Enter</span></td>
</tr>
<tr>
<td>Select a time range:</td>
<td><span class="key">⇧ Shift</span>+<span class="key">→</span> and <span class="key">⇧ Shift</span>+<span class="key">←</span></td>
</tr>
<tr>
<td>Edit an event:</td>
<td><span class="key">↵ Enter</span></td>
</tr>
</table>
<div class="toolbar">
Behavior:
<div class="toolbar-item">
<input type="checkbox" id="inline" checked> <label for="inline">Inline editing</label>
</div>
<div class="toolbar-separator"></div>
<div class="toolbar-item">
Inline edit input min width:
<select id="min">
<option value="0">matches cell</option>
<option value="100" selected>100px</option>
</select>
</div>
</div>
<div id="dp"></div>
<div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>
<script>
var dp = new DayPilot.Scheduler("dp", {
timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
scale: "Day",
days: DayPilot.Date.today().daysInYear(),
startDate: DayPilot.Date.today().firstDayOfYear(),
timeRangeSelectedHandling: "Enabled",
eventEditMinWidth: 100,
onTimeRangeSelected: function (args) {
var dp = this;
if (elements.inline.checked) {
dp.clearSelection();
var e = new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: "New event",
temporary: true
});
dp.events.add(e);
dp.events.edit(e);
}
else {
DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
dp.clearSelection();
if (!modal.result) { return; }
dp.events.add(new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
}));
});
}
},
onEventEdited: function(args) {
if (args.canceled && args.e.data.temporary) {
dp.events.remove(args.e);
}
else {
args.e.data.temporary = false;
}
},
onEventClick: function(args) {
if (elements.inline.checked) {
dp.events.edit(args.e);
}
else {
DayPilot.Modal.prompt("Edit event:", args.e.data.text).then(function(modal) {
if (modal.canceled) {
return;
}
args.e.data.text = modal.result;
dp.events.update(args.e);
});
}
},
treeEnabled: true,
keyboardEnabled: true,
height: 300
});
dp.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"},
{name: "Resource 10", id: "R10"},
{name: "Resource 11", id: "R11"},
{name: "Resource 12", id: "R12"},
{name: "Resource 13", id: "R13"},
];
dp.events.list = [];
dp.scrollTo(DayPilot.Date.today());
dp.init();
var elements = {
inline: document.querySelector("#inline"),
min: document.querySelector("#min")
};
elements.min.addEventListener("change", function(ev) {
dp.eventEditMinWidth = parseInt(elements.min.value);
});
</script>
</body>
</html>