Popular features/Schedule direction
Loading live demo…from dhtmlxcode.com
Popular features
Schedule direction
Plan from the start date or back from the deadline, and compare dependency only scheduling with task constraints.
gantt.plugins({
auto_scheduling: true,
marker: true,
tooltip: true
});
gantt.message({
text: "Switch the direction to move spare time between the project edges.<br><br>Disable constraints to compare dependency-only scheduling with constrained scheduling.<br><br>Double-click a task to edit its constraint.",
expire: -1
});
gantt.config.date_format = "%Y-%m-%d";
gantt.config.work_time = true;
gantt.config.fit_tasks = false;
gantt.config.row_height = 36;
gantt.config.bar_height = 24;
gantt.config.scale_height = 52;
gantt.config.min_column_width = 34;
gantt.config.grid_width = 650;
gantt.config.grid_resize = true;
function mondayOnOrAfter(date) {
date = gantt.date.day_start(new Date(date));
while (date.getDay() !== 1) date = gantt.date.add(date, 1, "day");
return date;
}
const PROJECT_START = mondayOnOrAfter(new Date());
const PROJECT_END = gantt.date.add(PROJECT_START, 46, "day");
const dateToData = gantt.date.date_to_str("%Y-%m-%d");
const dateToGrid = gantt.date.date_to_str("%d %M");
const SEED = dateToData(PROJECT_START);
function offsetFromStart(days) {
return gantt.date.add(PROJECT_START, days, "day");
}
function offsetFromEnd(days) {
return gantt.date.add(PROJECT_END, days, "day");
}
gantt.config.project_start = PROJECT_START;
gantt.config.project_end = PROJECT_END;
gantt.config.start_date = gantt.date.add(PROJECT_START, -2, "day");
gantt.config.end_date = gantt.date.add(PROJECT_END, 4, "day");
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "day", step: 1, format: "%j %D" }
];
gantt.templates.scale_cell_class = function (date) {
return gantt.isWorkTime(date) ? "" : "weekend";
};
gantt.templates.timeline_cell_class = function (task, date) {
return gantt.isWorkTime({ date: date, task: task }) ? "" : "weekend";
};
const constraintLabels = {
asap: "ASAP",
alap: "ALAP",
snet: "SNET",
snlt: "SNLT",
fnet: "FNET",
fnlt: "FNLT",
mso: "MSO",
mfo: "MFO"
};
function constraintTemplate(task) {
if (task.type === gantt.config.types.project || !task.constraint_type) return "";
const type = constraintLabels[task.constraint_type] || String(task.constraint_type).toUpperCase();
const date = task.constraint_date ? ` · ${dateToGrid(task.constraint_date)}` : "";
const exact = task.constraint_type === "mso" || task.constraint_type === "mfo";
return `<div class="constraint_cell"><span class="constraint_badge${exact ? " constraint_badge--exact" : ""}">${type}${date}</span></div>`;
}
gantt.config.columns = [
{ name: "text", label: "Task", tree: true, width: 220, resize: true },
{ name: "start_date", label: "Start", align: "center", width: 90, resize: true,
template: function (task) { return task.type === gantt.config.types.project ? "" : gantt.templates.date_grid(task.start_date, task); } },
{ name: "end_date", label: "End", align: "center", width: 90, resize: true,
template: function (task) { return task.type === gantt.config.types.project ? "" : gantt.templates.date_grid(task.end_date, task); } },
{ name: "duration", label: "Days", align: "center", width: 55, resize: true,
editor: { type: "number", map_to: "duration", min: 1, max: 60 } },
{ name: "constraint", label: "Constraint", align: "center", width: 155, resize: true, template: constraintTemplate },
{ name: "add", width: 40, resize: true }
];
gantt.locale.labels.section_constraint = "Constraint";
gantt.config.lightbox.sections = [
{ name: "description", height: 44, map_to: "text", type: "textarea", focus: true },
{ name: "constraint", type: "constraint" },
{ name: "time", map_to: "auto", type: "duration" }
];
gantt.config.lightbox.project_sections = [
{ name: "description", height: 44, map_to: "text", type: "textarea", focus: true },
{ name: "time", map_to: "auto", type: "duration" }
];
const data = {
tasks: [
{ id: 1, text: "Website Relaunch", type: "project", open: true, progress: 0.3 },
{ id: 11, text: "Kickoff & scope", parent: 1, start_date: SEED, duration: 3, progress: 0.6 },
{ id: 12, text: "UX & visual design", parent: 1, start_date: SEED, duration: 6, progress: 0.3 },
{ id: 13, text: "Front-end build", parent: 1, start_date: SEED, duration: 8, progress: 0 },
{ id: 14, text: "Content & SEO", parent: 1, start_date: SEED, duration: 5, progress: 0,
constraint_type: "snet", constraint_date: offsetFromStart(14) },
{ id: 15, text: "QA & fixes", parent: 1, start_date: SEED, duration: 4, progress: 0 },
{ id: 16, text: "Launch prep", parent: 1, start_date: SEED, duration: 2, progress: 0,
constraint_type: "fnet", constraint_date: offsetFromStart(30) },
{ id: 17, text: "Go live", parent: 1, start_date: SEED, type: "milestone",
constraint_type: "mso", constraint_date: offsetFromStart(40) },
{ id: 2, text: "Marketing & Launch Campaign", type: "project", open: true, progress: 0.2 },
{ id: 21, text: "Campaign kickoff & brief", parent: 2, start_date: SEED, duration: 2, progress: 0.5 },
{ id: 22, text: "Brand messaging & assets", parent: 2, start_date: SEED, duration: 5, progress: 0.2 },
{ id: 23, text: "Landing page copy", parent: 2, start_date: SEED, duration: 4, progress: 0 },
{ id: 24, text: "Paid media plan", parent: 2, start_date: SEED, duration: 3, progress: 0 },
{ id: 25, text: "Social content calendar", parent: 2, start_date: SEED, duration: 4, progress: 0 },
{ id: 26, text: "Email sequence draft", parent: 2, start_date: SEED, duration: 3, progress: 0 },
{ id: 27, text: "Influencer outreach", parent: 2, start_date: SEED, duration: 4, progress: 0 },
{ id: 28, text: "Ad creative production", parent: 2, start_date: SEED, duration: 5, progress: 0 },
{ id: 29, text: "Landing page build", parent: 2, start_date: SEED, duration: 6, progress: 0 },
{ id: 30, text: "Analytics & tracking setup", parent: 2, start_date: SEED, duration: 2, progress: 0 },
{ id: 31, text: "Internal review & sign-off", parent: 2, start_date: SEED, duration: 2, progress: 0 },
{ id: 32, text: "Press release draft", parent: 2, start_date: SEED, duration: 3, progress: 0 },
{ id: 33, text: "Legal & compliance check", parent: 2, start_date: SEED, duration: 2, progress: 0,
constraint_type: "snlt", constraint_date: offsetFromEnd(-16) },
{ id: 34, text: "Channel scheduling", parent: 2, start_date: SEED, duration: 2, progress: 0 },
{ id: 35, text: "Final QA pass", parent: 2, start_date: SEED, duration: 3, progress: 0,
constraint_type: "fnlt", constraint_date: offsetFromEnd(-7) },
{ id: 36, text: "Launch day coordination", parent: 2, start_date: SEED, duration: 1, progress: 0 },
{ id: 37, text: "Campaign live", parent: 2, start_date: SEED, type: "milestone",
constraint_type: "mfo", constraint_date: PROJECT_END }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 11, target: 14, type: "0" },
{ id: 4, source: 13, target: 15, type: "0" },
{ id: 5, source: 14, target: 15, type: "0" },
{ id: 6, source: 15, target: 16, type: "0" },
{ id: 7, source: 16, target: 17, type: "0" },
{ id: 8, source: 21, target: 22, type: "0" },
{ id: 9, source: 21, target: 23, type: "0" },
{ id: 10, source: 21, target: 24, type: "0" },
{ id: 11, source: 21, target: 30, type: "0" },
{ id: 12, source: 22, target: 28, type: "0" },
{ id: 13, source: 23, target: 29, type: "0" },
{ id: 14, source: 24, target: 25, type: "0" },
{ id: 15, source: 25, target: 26, type: "0" },
{ id: 16, source: 26, target: 27, type: "0" },
{ id: 17, source: 27, target: 34, type: "0" },
{ id: 18, source: 30, target: 31, type: "0" },
{ id: 19, source: 31, target: 32, type: "0" },
{ id: 20, source: 32, target: 33, type: "0" },
{ id: 21, source: 28, target: 35, type: "0" },
{ id: 22, source: 29, target: 35, type: "0" },
{ id: 23, source: 34, target: 35, type: "0" },
{ id: 24, source: 33, target: 35, type: "0" },
{ id: 25, source: 35, target: 36, type: "0" },
{ id: 26, source: 36, target: 37, type: "0" }
]
};
const startMarkerId = gantt.addMarker({ start_date: PROJECT_START, text: "Project start", css: "anchor_marker" });
const endMarkerId = gantt.addMarker({ start_date: PROJECT_END, text: "Deadline", css: "anchor_marker" });
let currentDirection = "forward";
let constraintsEnabled = true;
function updateAnchorUI(fromEnd) {
const startMarker = gantt.getMarker(startMarkerId);
const endMarker = gantt.getMarker(endMarkerId);
if (startMarker) {
startMarker.css = "anchor_marker" + (fromEnd ? "" : " active_anchor");
gantt.updateMarker(startMarkerId);
}
if (endMarker) {
endMarker.css = "anchor_marker" + (fromEnd ? " active_anchor" : "");
gantt.updateMarker(endMarkerId);
}
DHX.ui.setActiveByAttribute("#dir_seg .dhx_btn", "data-dir", fromEnd ? "backward" : "forward", "active");
}
function configureAutoScheduling() {
const fromEnd = currentDirection === "backward";
gantt.config.auto_scheduling = {
enabled: true,
show_constraints: true,
gap_behavior: "compress",
apply_constraints: constraintsEnabled,
schedule_from_end: fromEnd
};
gantt.autoSchedule();
updateAnchorUI(fromEnd);
DHX.ui.setActiveByAttribute("#constraint_seg .dhx_btn", "data-constraints", constraintsEnabled ? "on" : "off", "active");
document.getElementById("direction_status").textContent =
`${fromEnd ? "Backward" : "Forward"} planning · constraints ${constraintsEnabled ? "applied" : "ignored"}`;
}
function applyDirection(direction) {
currentDirection = direction === "backward" ? "backward" : "forward";
configureAutoScheduling();
}
function setConstraints(enabled) {
constraintsEnabled = !!enabled;
configureAutoScheduling();
}
gantt.config.auto_scheduling = {
enabled: true,
show_constraints: true,
gap_behavior: "compress",
apply_constraints: true,
schedule_from_end: false
};
gantt.init("gantt_here");
gantt.parse(data);
configureAutoScheduling();<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Schedule direction & task constraints | dhtmlxGantt</title>
<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
<script src="../common/demo-controls/dhx_controls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body { display: flex; flex-direction: column; }
#gantt_here { flex: 1 1 auto; min-height: 0; width: 100%; }
.gantt_task_line { border: none; border-radius: 7px; box-shadow: 0 1px 2px rgba(16,24,40,.18); }
.gantt_task_line .gantt_task_progress { border-radius: 7px 0 0 7px; }
.gantt_task_line.gantt_project { border-radius: 5px; }
.gantt_task_cell.weekend,
.gantt_scale_cell.weekend { background-color: rgba(15,23,42,.04); }
.gantt_marker.anchor_marker { background: #b6bdc7; opacity: 1; }
.gantt_marker.anchor_marker .gantt_marker_content { color: #fff; font-weight: 600; }
.gantt_marker.anchor_marker.active_anchor { background: #e5484d; }
.schedule_header {
justify-content: center;
gap: 14px;
height: auto;
min-height: 52px;
flex-wrap: wrap;
box-sizing: border-box;
}
.toolbar_label { font: 600 13px var(--dhx-font); color: var(--dhx-ink-2); }
.constraint_badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 7px;
border-radius: 999px;
background: rgba(37,99,235,.09);
color: #1d4ed8;
font: 600 11px var(--dhx-font);
white-space: nowrap;
}
.constraint_badge--exact {
background: rgba(229,72,77,.10);
color: #c92f35;
}
:root[data-gantt-theme="dark"] .constraint_badge {
background: rgba(59,130,246,.16);
color: #9fc4ff;
}
:root[data-gantt-theme="dark"] .constraint_badge--exact {
background: rgba(255,107,110,.16);
color: #ff6b6e;
}
.constraint_cell { display: flex; align-items: center; justify-content: center; min-height: 100%; }
.footer_note { margin-left: auto; color: var(--dhx-muted); }
@media (max-width: 760px) {
.schedule_header > .toolbar_label { flex: 1 1 100%; text-align: center; }
.footer_note { display: none; }
}
</style>
</head>
<body>
<div class="dhx_header schedule_header">
<span class="toolbar_label">Plan the project</span>
<div class="dhx_seg" id="dir_seg">
<button class="dhx_btn active" data-dir="forward" onclick="applyDirection('forward')">From the start</button>
<button class="dhx_btn" data-dir="backward" onclick="applyDirection('backward')">From the deadline</button>
</div>
<span class="toolbar_label">Task constraints</span>
<div class="dhx_seg" id="constraint_seg">
<button class="dhx_btn" data-constraints="off" onclick="setConstraints(false)">Ignore</button>
<button class="dhx_btn active" data-constraints="on" onclick="setConstraints(true)">Apply</button>
</div>
</div>
<div id="gantt_here"></div>
<div class="dhx_footer">
<span id="direction_status">Forward planning · constraints applied</span>
<span class="footer_note">Constraint markers remain visible when their effect is disabled.</span>
</div>
</body>
</html>