Popular features/Critical path and slack
Loading live demo…from dhtmlxcode.com
Popular features
Critical path and slack
Highlight the chain of tasks that drives the finish date, and show how much room every other task still has.
gantt.plugins({ critical_path: true, tooltip: true, marker: true, auto_scheduling: true });
gantt.config.date_format = "%Y-%m-%d";
gantt.config.auto_types = true;
gantt.config.grid_resize = true;
gantt.config.open_tree_initially = true;
gantt.config.work_time = true;
gantt.config.auto_scheduling = {
enabled: true,
use_constraints: false,
gap_behavior: "compress"
};
gantt.config.highlight_critical_path = true;
gantt.config.row_height = 36;
gantt.config.bar_height = 22;
let dateToStr = gantt.date.date_to_str("%d %M");
/* ---------------------------------------------------------------------------
* DATASET: launch plan with a long critical chain and slack-bearing branches
* ------------------------------------------------------------------------ */
let demoData = {
tasks: [
{ id: 1, text: "Trade show launch", type: "project", open: true },
/* critical chain: every slip here moves the opening date */
{ id: 10, text: "Launch concept approved", parent: 1, start_date: "2026-09-01", duration: 15, progress: 1 },
{ id: 11, text: "Launch brief approved", parent: 1, start_date: "2026-09-01", duration: 5, progress: 1 },
{ id: 12, text: "Booth concept & layout", parent: 1, start_date: "2026-09-08", duration: 10, progress: 0.8 },
{ id: 13, text: "Vendor contract signed", parent: 1, start_date: "2026-09-22", duration: 7, progress: 0.6 },
{ id: 14, text: "Fabricate custom booth", parent: 1, start_date: "2026-10-01", duration: 25, progress: 0.35},
{ id: 15, text: "Factory acceptance review", parent: 1, start_date: "2026-11-05", duration: 6, progress: 0 },
{ id: 16, text: "Pack and freight booth", parent: 1, start_date: "2026-11-13", duration: 8, progress: 0 },
{ id: 17, text: "Ship to venue warehouse", parent: 1, start_date: "2026-11-25", duration: 10, progress: 0 },
{ id: 18, text: "Install booth on site", parent: 1, start_date: "2026-12-09", duration: 5, progress: 0 },
{ id: 19, text: "Executive demo rehearsal", parent: 1, start_date: "2026-12-16", duration: 3, progress: 0 },
{ id: 20, text: "Final launch readiness", parent: 1, start_date: "2026-12-21", duration: 10, progress: 0 },
{ id: 51, text: "Media walkthrough rehearsal", parent: 1, start_date: "2027-01-04", duration: 4, progress: 0 },
{ id: 52, text: "Final press-day sign-off", parent: 1, start_date: "2027-01-08", duration: 3, progress: 0 },
/* marketing can finish earlier; the dashed bar shows the room before launch */
{ id: 21, text: "Campaign messaging", parent: 1, start_date: "2026-09-22", duration: 8, progress: 0.7 },
{ id: 22, text: "Landing page and invites", parent: 1, start_date: "2026-10-02", duration: 10, progress: 0.4 },
{ id: 23, text: "Sales kit localization", parent: 1, start_date: "2026-10-16", duration: 8, progress: 0 },
{ id: 24, text: "Press briefing package", parent: 1, start_date: "2026-10-28", duration: 6, progress: 0 },
/* sponsor coordination starts early alongside marketing, and carries the most float */
{ id: 71, text: "Sponsor prospectus & tiers", parent: 1, start_date: "2026-10-01", duration: 9, progress: 0.2 },
{ id: 72, text: "Exhibitor floor coordination", parent: 1, start_date: "2026-10-14", duration: 7, progress: 0 },
/* staffing has less float, but still does not drive the opening date */
{ id: 31, text: "Book booth staff", parent: 1, start_date: "2026-11-05", duration: 7, progress: 0.3 },
{ id: 32, text: "Train booth staff", parent: 1, start_date: "2026-11-16", duration: 6, progress: 0 },
{ id: 33, text: "Publish booth schedule", parent: 1, start_date: "2026-11-24", duration: 4, progress: 0 },
/* partner demos are close to launch, so they carry only a small buffer */
{ id: 41, text: "Confirm partner demo slots", parent: 1, start_date: "2026-11-13", duration: 5, progress: 0.1 },
{ id: 42, text: "Load demo content", parent: 1, start_date: "2026-11-20", duration: 7, progress: 0 },
{ id: 43, text: "Partner dry run", parent: 1, start_date: "2026-12-01", duration: 4, progress: 0 },
{ id: 44, text: "Partner showcase walkthrough", parent: 1, start_date: "2026-12-07", duration: 3, progress: 0 },
{ id: 50, text: "Doors open", type: "milestone", parent: 1, start_date: "2027-01-13", progress: 0 }
],
links: [
/* critical chain */
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 13, target: 14, type: "0" },
{ id: 4, source: 14, target: 15, type: "0" },
{ id: 5, source: 15, target: 16, type: "0" },
{ id: 6, source: 16, target: 17, type: "0" },
{ id: 7, source: 17, target: 18, type: "0" },
{ id: 8, source: 18, target: 19, type: "0" },
{ id: 9, source: 19, target: 20, type: "0" },
{ id: 10, source: 20, target: 51, type: "0" },
{ id: 24, source: 51, target: 52, type: "0" },
{ id: 25, source: 52, target: 50, type: "0" },
/* slack-bearing branches also feed the opening milestone */
{ id: 11, source: 13, target: 21, type: "0" },
{ id: 12, source: 21, target: 22, type: "0" },
{ id: 13, source: 22, target: 23, type: "0" },
{ id: 14, source: 23, target: 24, type: "0" },
{ id: 15, source: 24, target: 50, type: "0" },
{ id: 16, source: 15, target: 31, type: "0" },
{ id: 17, source: 31, target: 32, type: "0" },
{ id: 18, source: 32, target: 33, type: "0" },
{ id: 19, source: 33, target: 50, type: "0" },
{ id: 20, source: 16, target: 41, type: "0" },
{ id: 21, source: 41, target: 42, type: "0" },
{ id: 22, source: 42, target: 43, type: "0" },
{ id: 23, source: 43, target: 44, type: "0" },
{ id: 26, source: 44, target: 50, type: "0" },
{ id: 27, source: 13, target: 71, type: "0" },
{ id: 28, source: 71, target: 72, type: "0" },
{ id: 29, source: 72, target: 50, type: "0" },
{ id: 30, source: 10, target: 14, type: "0" },
{ id: 31, source: 10, target: 21, type: "0" }
]
};
/* ---------------------------------------------------------------------------
* GRID
* ------------------------------------------------------------------------ */
function slackChip(task) {
if (gantt.isSummaryTask(task) || task.type === gantt.config.types.milestone) return "";
let slack = Math.round(gantt.getTotalSlack(task));
let cls = slack <= 0 ? "crit" : (slack <= 3 ? "tight" : "free");
let label = slack <= 0 ? "0d" : `${slack}d`;
return `<span class='slack_chip ${cls}'>${label}</span>`;
}
gantt.config.columns = [
{ name: "text", label: "Task", tree: true, width: 220, resize: true },
{ name: "start_date", label: "Start", align: "center", width: 100, resize: true, template: function (task) {
return task.type === gantt.config.types.project ? "" : gantt.templates.date_grid(task.start_date, task);
}},
{ name: "duration", label: "Days", align: "center", width: 56, resize: true, template: function (task) {
return task.type === gantt.config.types.milestone ? "" : task.duration;
}},
{ name: "slack", label: "Slack", align: "center", width: 76, resize: true, template: slackChip },
{ name: "add", width: 40, resize: true }
];
gantt.config.grid_width = 470;
gantt.templates.tooltip_text = function (start, end, task) {
if (task.type === gantt.config.types.project) return `<b>${task.text}</b>`;
let crit = gantt.isCriticalTask(task);
let slack = Math.round(gantt.getTotalSlack(task));
return `<b>${task.text}</b><br>${dateToStr(start)} – ${dateToStr(end)}<br>` +
(crit ? "On the critical path" : `Total slack: ${slack} working day${slack === 1 ? "" : "s"}`);
};
/* One readable launch-window scale: the critical chain and float are visible on load. */
gantt.config.scale_height = 50;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: "Wk %W" }
];
/* ---------------------------------------------------------------------------
* SLACK FLOAT LAYER (dashed bar trailing each non-critical task)
* ------------------------------------------------------------------------ */
let slackOn = true;
let MILESTONE_ID = 50; // "Doors open": slack chains all terminate here
gantt.addTaskLayer(function (task) {
if (!slackOn) return null;
if (gantt.isSummaryTask(task) || task.type === gantt.config.types.milestone) return null;
let drag = gantt.getState().drag_mode;
if (drag === "resize" || drag === "move") return null;
let slack = Math.round(gantt.getTotalSlack(task));
if (slack <= 0) return null;
let slackEnd = gantt.calculateEndDate(task.end_date, slack); // positional: (start, duration)
let pos = gantt.getTaskPosition(task, task.end_date, slackEnd);
if (!pos.width) return null;
/* stop the float bar just short of the milestone so it never overlaps the diamond */
let msX = gantt.posFromDate(gantt.getTask(MILESTONE_ID).start_date);
let minWidth = 34;
let barWidth = Math.max(pos.width, minWidth);
let right = Math.min(pos.left + barWidth, msX - 10);
let width = right - pos.left;
if (width < 6) return null;
/* getTaskPosition().top is the ROW top; dhtmlx centres the bar with this pad */
let pad = Math.floor(((pos.rowHeight || gantt.config.row_height) - pos.height) / 2);
let bar = document.createElement("div");
bar.className = "slack_bar";
let label = document.createElement("span");
label.className = "slack_bar__label";
label.textContent = "+" + slack + "d";
bar.appendChild(label);
bar.style.left = pos.left + "px";
bar.style.top = (pos.top + pad) + "px"; // match the taskbar's exact top/height
bar.style.width = width + "px";
bar.style.height = pos.height + "px";
return bar;
});
/* ---------------------------------------------------------------------------
* TOGGLES
* ------------------------------------------------------------------------ */
function toggleCritical() {
gantt.config.highlight_critical_path = !gantt.config.highlight_critical_path;
DHX.toggle.setById("cp_switch", gantt.config.highlight_critical_path);
gantt.render();
}
function toggleSlack() {
slackOn = !slackOn;
DHX.toggle.setById("slack_switch", slackOn);
gantt.render();
}
function addProjectMarkers() {
gantt.addMarker({
start_date: PROJECT_START,
css: "project_marker project_start_marker",
text: "Project start",
title: "Project start: " + dateToStr(PROJECT_START)
});
gantt.addMarker({
start_date: PROJECT_END,
css: "project_marker project_end_marker",
text: "Project end",
title: "Project end: " + dateToStr(PROJECT_END)
});
}
/* ---------------------------------------------------------------------------
* INIT
* ------------------------------------------------------------------------ */
let PROJECT_START = new Date(2026, 8, 1);
let PROJECT_END = new Date(2027, 0, 13);
gantt.init("gantt_here");
gantt.parse(demoData);
addProjectMarkers();
gantt.showDate(PROJECT_START);<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Critical path & slack: 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 { width: 100%; flex: 1 1 auto; min-height: 0; }
.gantt_task_line { z-index: 2; }
.gantt_critical_link div{
z-index: 1;
}
.slack_bar {
position: absolute; z-index: 1; box-sizing: border-box;
border: 1px dashed #16a34a; border-left: none;
background: repeating-linear-gradient(45deg, rgba(22,163,74,.10) 0 6px, rgba(22,163,74,.02) 6px 12px);
border-radius: 0 4px 4px 0; pointer-events: none;
font: 600 10px var(--dhx-font); color: #16a34a; white-space: nowrap;
overflow: visible;
}
.slack_bar__label {
position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
padding: 0 2px; border-radius: 2px;
background: #fff; line-height: 12px;
}
:root[data-gantt-theme="dark"] .slack_bar {
border-color: #4ade80;
background: repeating-linear-gradient(45deg, rgba(74,222,128,.22) 0 6px, rgba(74,222,128,.05) 6px 12px);
color: #69d391;
}
:root[data-gantt-theme="dark"] .slack_bar__label {
background: var(--dhx-gantt-base-colors-background);
}
.slack_chip { display: inline-block; padding: 1px 9px; border-radius: 10px; font: 600 11px var(--dhx-font); font-variant-numeric: tabular-nums; }
.slack_chip.crit { background: var(--dhx-danger-bg); color: var(--dhx-danger-ink); }
.slack_chip.tight { background: var(--dhx-warn-bg); color: var(--dhx-warn-ink); }
.slack_chip.free { background: var(--dhx-ok-bg); color: var(--dhx-ok-ink); }
.cp_header { position: relative; }
.cp_header .dhx_header__center { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; }
.gantt_marker.project_marker { width: 2px; background: #f59e0b; }
.gantt_marker.project_marker .gantt_marker_content {
top: 6px; left: 0; transform: translateX(-1px);
padding: 3px 7px; border-radius: 3px;
background: #f59e0b; color: #fff;
font: 600 11px var(--dhx-font); white-space: nowrap;
}
.gantt_marker.project_end_marker .gantt_marker_content {
left: auto; right: 0; transform: translateX(1px);
}
</style>
</head>
<body>
<!-- Header -->
<div class="dhx_header cp_header">
<div class="dhx_demo_hint">Red tasks affect the launch date. Dashed bars show available slack.</div>
<div class="dhx_header__center">
<label class="dhx_toggle active" onclick="toggleCritical()">
<span>Critical path</span><span class="dhx_toggle__track on" id="cp_switch"></span>
</label>
<label class="dhx_toggle active" onclick="toggleSlack()">
<span>Show slack (float)</span><span class="dhx_toggle__track on" id="slack_switch"></span>
</label>
</div>
</div>
<div id="gantt_here"></div>
</body>
</html>